//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//

#include "precomp.h"
#include "simmgr.h"

#ifdef TBD_DEBUG
#include "rilversion.h"
#endif

extern BOOL g_fAudioPathActivated;

HANDLE g_hSIMUnlockThread, g_hSIMUnlockEvent;
HANDLE g_hAckSMSThread, g_hAckSMSEvent;

#ifdef WANT_FILE_RILDRV_OUTPUT
DWORD g_dwDisableDebugFile = 0;
CRITICAL_SECTION    csDbgCritSect;
#endif
DWORD g_dwEnableGPRSAttach = 0;
DWORD g_dwEnableSNFASetting = 0;
BOOL g_fReportLocationUpdateAsRegistrationState = FALSE;
UINT g_iNumPBEntries = 0;
DWORD g_dwModemType = MODEMTYPE_UNKNOWN;
DWORD g_dwModemVersion = 0;
DWORD g_dwSIMLockedState = RIL_LOCKEDSTATE_UNKNOWN;
BOOL g_fReceiveCellBroadCast = TRUE;
char g_szReceiveCellBroadCastChannels[MAX_CBM_STRING];
char g_szReceiveCellBroadCastDCS[MAX_CBM_STRING];
BOOL g_fDisableCOLPInformation = FALSE;
UINT g_nCOLPStatus = 0;
extern RILSUBSCRIBERINFO g_rsi[8];
extern DWORD g_rsiused;
extern DWORD g_dwLastSMSCmd;
extern DWORD g_dwPreferredOperatorListStart;
extern DWORD g_dwCurrentPhoneBook;
extern UINT g_iSetCOPSinInit;
extern char g_szSetCOPSinInitOperator[MAXLENGTH_OPERATOR_NUMERIC+1];
extern UINT g_iLastCOPS;
#ifdef RIL_FAKECSQ
extern volatile BOOL g_fFakeSignalStrength;
#endif

extern "C" {
void fnRHACallback(DWORD dwEventMask);
}

HRESULT ParseGetPreferredOperatorListSize(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam);

#if defined(RIL_SYSTEMTYPE_EDGE)
DWORD WINAPI CheckEDGEStatusThreadProc(LPVOID pVoid);
extern UINT g_dwLastEDGEStatus;
#endif
HRESULT ParseGetPhoneLockedState(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam);
DWORD WINAPI SIMUnlockThreadProc(LPVOID lpParameter);
DWORD WINAPI AckSMSThreadProc(LPVOID lpParameter);
DWORD WINAPI GPRSReAttachThreadProc(LPVOID pVoid);
LPCTSTR g_tszRegKeyIntrinsycRIL = TEXT("Software\\RIL");
LPCTSTR g_tszRegKeyIntrinsycRILBand = TEXT("Software\\RIL\\Band");
LPCTSTR g_tszRegKeyIntrinsycRILHandover = TEXT("Software\\RIL\\Handover");
extern BOOL g_bCINDIsActive;
extern UINT g_iLastRSSI;
extern UINT g_iLastSignal;

// RIL registry key
const LPCTSTR g_tszRegKeyRIL = TEXT("Software\\Microsoft\\RIL");

// RIL API info registry key
const LPCTSTR g_tszRegKeyAPIInfo = TEXT("Software\\Microsoft\\RIL\\APIInfo\\");

// RIL opertor names registry key
const LPCTSTR g_tszRegKeyOperNames = TEXT("Software\\Microsoft\\RIL\\OperatorNames");

const HRESULT INITIAL_COMMAND_ID = 1;

DWORD g_TimeoutCmdInit;
DWORD g_TimeoutAPIDefault;

DWORD g_iCurrentSIMStatus = 0;

// It's safe to put these params in globals since the SIM PW prompt thread is only launched from the command thread,
// and no more than one SIM PW prompt thread at a time is launched.
BOOL g_PromptingForSim=FALSE;
DWORD g_dwLockFacility;
CQueue<CCommand, 10>* g_pSimLockedQueue;

// Interface to RHA.DLL
RHA_FUNCTION_LIST RHAFunctionList;
HINSTANCE   g_hRHAInst = NULL;

//
// RIL handle ctor
//
CRilHandle::CRilHandle()
: m_hInitEvent(NULL),
m_hDataEvent(NULL),
m_hCancelEvent(NULL),
m_hSimReadyEvent(NULL),
m_hInternalSimReadyEvent(NULL),
m_hAbortInternalSimReadyEvent(NULL),
m_fQuitReadyStateThread(FALSE),
m_pCheckPoint(NULL),
m_pMonitor(NULL),
m_pInstances(NULL),
m_hrNextCmdID(INITIAL_COMMAND_ID),
m_fInited(FALSE),
m_fFailedInit(FALSE),
m_fEmergencyMode(FALSE),
m_fSimReady(FALSE),
m_fIgnoreNextNOCARRIER(FALSE),
m_fSimulatedCLIPActive(TRUE),
m_fSMSURCInitPhase(FALSE),
m_dwAudioRxDevice(RIL_AUDIO_HANDSET),
m_dwAudioTxDevice(RIL_AUDIO_HANDSET),
m_fAllowData(FALSE),
m_pSimToolkitHandling(NULL),
m_pEonsHandling(NULL),
m_pRsaHandling(NULL)
{
    // // TBD_FUNCTION(CRilHandle::CRilHandle);
    // Intialize critical sections
    InitializeCriticalSection(&m_cs);
    InitializeCriticalSection(&m_csCmdID);
    InitializeCriticalSection(&m_csEmergency);
    InitializeCriticalSection(&m_csATTimeouts);
#ifdef WANT_FILE_RILDRV_OUTPUT
    InitializeCriticalSection(&csDbgCritSect);
#endif

    memset(m_rgpAPIInfo, 0x00, APIID_TOTAL * sizeof(APIINFO*));
    memset(m_pComDevice, 0x00, sizeof(m_pComDevice));
    memset(m_pReadBytes, 0x00, sizeof(m_pReadBytes));
    memset(m_fWaitingForRsp, 0x00, sizeof(m_fWaitingForRsp));
    memset(m_fLastCommandTimedOut, 0x00, sizeof(m_fLastCommandTimedOut));
    memset((void*)m_fUSSDAnswerMode, 0x00, sizeof(m_fUSSDAnswerMode));
    memset((void*)m_hCmdThread, 0x00, sizeof(m_hCmdThread));
    memset((void*)m_hReadThread, 0x00, sizeof(m_hReadThread));
    memset((void*)m_dwCmdThreadID, 0x00, sizeof(m_dwCmdThreadID));
    memset((void*)m_dwReadThreadID, 0x00, sizeof(m_dwReadThreadID));
    memset((void*)g_QBlockingData, 0x00, sizeof(g_QBlockingData));
    memset((void*)m_hBlockCmdsEvent, 0x00, sizeof(m_hBlockCmdsEvent));
    memset((void*)m_hBlockRspsEvent, 0x00, sizeof(m_hBlockRspsEvent));
    memset((void*)m_dwPendingPortCalls, 0x00, sizeof(m_dwPendingPortCalls));

    m_pEonsHandling = new CEonsHandling();
    m_pSimToolkitHandling = new CSimToolkitHandling();
    // Initialize the RSA capability
    m_pRsaHandling = new CRsaHandling();
    m_pRsaHandling->Init(this);


    g_fReportLocationUpdateAsRegistrationState = FALSE;
    g_bCINDIsActive = FALSE;
    g_iLastRSSI = 99;
    g_iLastSignal = 99;
    g_dwLastSMSCmd = 0;
    m_szLastDialedNumber[0] = '\0';
#ifdef RIL_USE_CPIN2_WORKAROUND
    ClearPIN2Secure();
#endif

    RHAFunctionList.pfnRHA_Init = NULL;
    RHAFunctionList.pfnRHA_DeInit = NULL;
    RHAFunctionList.pfnRHA_RadioPwrOn = NULL;
    RHAFunctionList.pfnRHA_RadioPwrOff = NULL;
    RHAFunctionList.pfnRHA_IsRadioPwrOn = NULL;
    RHAFunctionList.pfnRHA_ActivateVoicePath = NULL;
    RHAFunctionList.pfnRHA_DeactivateVoicePath = NULL;
    RHAFunctionList.pfnRHA_AdjustTxVolume = NULL;
    RHAFunctionList.pfnRHA_AdjustRxVolume = NULL;
    RHAFunctionList.pfnRHA_GetRHAVersion = NULL;
    RHAFunctionList.pfnRHA_ConfirmAudioMode = NULL;
    RHAFunctionList.pfnRHA_SystemPowerChange = NULL;
    g_hRHAInst = LoadLibrary(TEXT("RHA.DLL"));
    if(g_hRHAInst == NULL)
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, (TEXT("Error: Cannot load RHA.DLL")));
    }
    else
    {
        RHAFunctionList.pfnRHA_Init = (PFNRHA_INIT_DEINIT)GetProcAddress(g_hRHAInst, RHA_INIT);
        RHAFunctionList.pfnRHA_DeInit = (PFNRHA_INIT_DEINIT)GetProcAddress(g_hRHAInst, RHA_DEINIT);
        RHAFunctionList.pfnRHA_RadioPwrOn = (PFNRHA_RADIO_POWER_ON)GetProcAddress(g_hRHAInst, RHA_RADIO_POWER_ON);
        RHAFunctionList.pfnRHA_RadioPwrOff = (PFNRHA_RADIO_POWER_OFF)GetProcAddress(g_hRHAInst, RHA_RADIO_POWER_OFF);
        RHAFunctionList.pfnRHA_IsRadioPwrOn = (PFNRHA_IS_RADIO_POWER_ON)GetProcAddress(g_hRHAInst, RHA_IS_RADIO_POWER_ON);
        RHAFunctionList.pfnRHA_ActivateVoicePath = (PFNRHA_ACTIVATE_VOICE_PATH)GetProcAddress(g_hRHAInst, RHA_ACTIVATE_VOICE_PATH);
        RHAFunctionList.pfnRHA_DeactivateVoicePath = (PFNRHA_DEACTIVATE_VOICE_PATH)GetProcAddress(g_hRHAInst, RHA_DEACTIVATE_VOICE_PATH);
        RHAFunctionList.pfnRHA_AdjustTxVolume = (PFNRHA_ADJUST_TX_VOLUME)GetProcAddress(g_hRHAInst, RHA_ADJUST_TX_VOLUME);
        RHAFunctionList.pfnRHA_AdjustRxVolume = (PFNRHA_ADJUST_RX_VOLUME)GetProcAddress(g_hRHAInst, RHA_ADJUST_RX_VOLUME);
        RHAFunctionList.pfnRHA_GetRHAVersion = (PFNRHA_GET_RHA_VERSION)GetProcAddress(g_hRHAInst, RHA_GET_RHA_VERSION);
        RHAFunctionList.pfnRHA_ConfirmAudioMode = (PFNRHA_CONFIRM_AUDIO_MODE)GetProcAddress(g_hRHAInst, RHA_CONFIRM_AUDIO_MODE);
        RHAFunctionList.pfnRHA_SystemPowerChange = (PFNRHA_SYSTEM_POWER_CHANGE)GetProcAddress(g_hRHAInst, RHA_SYSTEM_POWER_CHANGE);
    }
    if (RHAFunctionList.pfnRHA_Init != NULL)
   	{
        RHAFunctionList.pfnRHA_Init();
	    RILRetailTrace((TEXT("[RIL]:pfnRHA_Init Completed \n")));		
   	}
	else
	{
	    RILRetailTrace((TEXT("[RIL]:Failed in pfnRHA_Init == NULL \n")));		
	}
}


//
// Function passed to CDlbList::Enum() below
//
BOOL DeleteInstanceHandle(void* pItem, DWORD dwData)
{
    TBD_ASSERT(pItem != NULL);

    CRilInstanceHandle* pHandle = (CRilInstanceHandle*)pItem;
    delete pHandle;

    // Continue enumeration
    return FALSE;
}

//
// RIL handle dtor
//
CRilHandle::~CRilHandle()
{
    // // TBD_FUNCTION(CRilHandle::~CRilHandle);

    UINT i;

    {
        SYNCBLOCK(m_cs);

        // Stop the RSA handling
        m_pRsaHandling->Deinit();
        delete m_pRsaHandling;
        
        // Stop the SIM-Toolkit handling
        m_pSimToolkitHandling->Deinit();
        delete m_pSimToolkitHandling;

        // Stop the EONS handling
        m_pEonsHandling->Deinit();
        delete m_pEonsHandling;

        // Signal the cancel event to kill the thread
        if (m_hCancelEvent)
        {
            (void)SetEvent(m_hCancelEvent);
        }

        StopReadyStateQuery();

        // Wait for auxiliary threads to terminate
        for (i = 0; i < NUMBER_OF_PORTS; i++) {
          (void)WaitForSingleObject(m_hCmdThread[i], 10000);
          (void)WaitForSingleObject(m_hReadThread[i], 10000);
        }

        // Get rid of the threads
        for (i = 0; i < NUMBER_OF_PORTS; i++) {
          if (m_hCmdThread[i])
          {
            (void)CloseHandle(m_hCmdThread[i]);
            m_hCmdThread[i] = NULL;
            m_dwCmdThreadID[i] = 0;
          }
          if (m_hReadThread[i])
          {
            (void)CloseHandle(m_hReadThread[i]);
            m_hReadThread[i] = NULL;
            m_dwReadThreadID[i] = 0;
          }
        }

        if (g_hAckSMSEvent)
        {
            (void)CloseHandle(g_hAckSMSEvent);
            g_hAckSMSEvent = NULL;
        }
        if (g_hSIMUnlockEvent)
        {
            SetEvent(g_hSIMUnlockEvent);    // Kill the unlock thread
            Sleep(10);
            (void)CloseHandle(g_hSIMUnlockEvent);
            g_hSIMUnlockEvent = NULL;
        }
        if (g_hSIMUnlockThread)
        {
            (void)CloseHandle(g_hSIMUnlockThread);
            g_hSIMUnlockThread = NULL;
        }

        // Get rid of the event
        if (m_hCancelEvent)
        {
            (void)CloseHandle(m_hCancelEvent);
            m_hCancelEvent = NULL;
        }

        // Get rid of the checkpoint
        delete m_pCheckPoint;
        m_pCheckPoint = NULL;

        // Get rid of the monitor
        delete m_pMonitor;
        m_pMonitor = NULL;

        // Get rid of the queues
        for (i = 0; i < NUMBER_OF_PORTS; i++) {
          if (g_QBlockingData[i].hEvents[0]) {
            CloseHandle(g_QBlockingData[i].hEvents[0]);
            g_QBlockingData[i].hEvents[0] = NULL;
          }
          if (g_QBlockingData[i].hEvents[1]) {
            CloseHandle(g_QBlockingData[i].hEvents[1]);
            g_QBlockingData[i].hEvents[1] = NULL;
          }
          DeleteCriticalSection(&g_QBlockingData[i].csLock);
          delete g_pCmdQ[i];
          delete g_pRspQ[i];
          g_pCmdQ[i] = NULL;
          g_pRspQ[i] = NULL;
        }

        delete g_pSimLockedQueue;
        g_pSimLockedQueue = NULL;

        // Close the serial ports
        for (i = 0; i < NUMBER_OF_PORTS; i++) {
          if (m_pComDevice[i])
          {
            m_pComDevice[i]->SetExternalCancelEvent(NULL);
            g_ComManager.DestroyPortDevice((COM_PORT_TYPE)i);
            m_pComDevice[i] = NULL;
          }
        }

        // All of the instance handles should be closed
        // before the driver handle is destroyed.
        m_pInstances->Enum(DeleteInstanceHandle, 0);

        // Delete the instance handle list
        delete m_pInstances;
        m_pInstances = NULL;

        // Delete the Read Bytes buffers
        for (i = 0; i < NUMBER_OF_PORTS; i++) {
          if (m_pReadBytes[i]) {
            delete m_pReadBytes[i];
            m_pReadBytes[i] = NULL;
           }
        }

        // Get rid of remaining events
        if (m_hInitEvent) {
            (void)CloseHandle(m_hInitEvent);
            m_hInitEvent = NULL;
        }

        // Get rid of data event
        if (m_hDataEvent)
        {
            (void)CloseHandle(m_hDataEvent);
            m_hDataEvent = NULL;
        }
        
        for (i = 0; i < NUMBER_OF_PORTS; i++) {
            if (m_hBlockCmdsEvent[i]) {
                CloseHandle(m_hBlockCmdsEvent[i]);
                m_hBlockCmdsEvent[i] = NULL;
            }
            if (m_hBlockRspsEvent[i]) {
                CloseHandle(m_hBlockRspsEvent[i]);
                m_hBlockRspsEvent[i] = NULL;
            }
            DeleteCriticalSection(&(m_csBlockCmds[i]));
        }
        for (i = 0; i < APIID_TOTAL; i++)
        {
            delete m_rgpAPIInfo[i];
            m_rgpAPIInfo[i] = NULL;
        }

        if(g_hRHAInst != NULL) {
            if (RHAFunctionList.pfnRHA_DeInit != NULL)
                RHAFunctionList.pfnRHA_DeInit();
            FreeLibrary(g_hRHAInst);
            g_hRHAInst = NULL;
        }

        DeleteCriticalSection(&m_csATTimeouts);
        DeleteCriticalSection(&m_csCmdID);
        DeleteCriticalSection(&m_csEmergency);
#ifdef WANT_FILE_RILDRV_OUTPUT
        DeleteCriticalSection(&csDbgCritSect);
#endif
        m_fInited = FALSE;
        m_fFailedInit=FALSE;
    }

    DeleteCriticalSection(&m_cs);
}

//
// Broadcasts a change in the radio ready state
//
void CRilHandle::SendReadyStateNotification(DWORD dwReadyState, BOOL fBroadcast)
{
    RILEQUIPMENTSTATE res;
    CNotificationData *pnd = NULL;

    // Set this in our global
    if (dwReadyState)
    {
        g_dwReadyState |= dwReadyState;
    }
    else
    {
        g_dwReadyState = 0;
    }

    if (fBroadcast)
    {
        // Broadcast the notification
        memset(&res, 0x00, sizeof(RILEQUIPMENTSTATE));
        res.cbSize = sizeof(RILEQUIPMENTSTATE);
        res.dwParams = RIL_PARAM_EQUIPMENTSTATE_READYSTATE;
        res.dwReadyState = g_dwReadyState;
        pnd = new CNotificationData;
        if (pnd && pnd->InitFromRealBlob(RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED, (void *) &res, res.cbSize))
        {
            // Note that BroadcastNotification frees pnd
            BroadcastNotification(pnd);
            pnd = NULL;
        }

        delete pnd;
    }

    // Now, update StatStor
    UpdateStatStore();
}

//
// Thread responsible for sending commands from the Command Queue to COM port
//
DWORD WINAPI SimReadyThreadProc(LPVOID lpParameter)
{
    TBD_FUNCTION(SimReadyThreadProc);
    TBD_ASSERT(lpParameter != NULL);
    CRilHandle* pRilDevice = (CRilHandle*)lpParameter;

    if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, (TEXT("SimReadyThreadProc : Unable to raise priority of read thread!!")));
        return 0;
    }

    TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("SimReadyThreadProc : Entering, pRilDevice=0x%x"),pRilDevice);
    const DWORD dwReturn = pRilDevice->SimReadyThread();
    TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("SimReadyThreadProc : THREAD IS EXITING, dwReturn=0x%x"),dwReturn);
    return dwReturn;
}

//
// Loop until RIL is ready to process SIM commands
//
DWORD CRilHandle::SimReadyThread()
{
    APIINFO apiiInfo;
    const APIID apiid = APIID_UNLOCKPHONE;
    DWORD dwSimReadyRetry = 0;

    // Create an event
    m_fSimReady = FALSE;
    m_hSimReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!m_hSimReadyEvent)
    {
        goto Error;
    }

    // Get the info about this API
    memset(&apiiInfo,0,sizeof(apiiInfo));
    GetAPIInfo(apiid, apiiInfo);

    // Query for the PIN until we don't get back 515 as an error
    do
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("SimReadyThread : Sending SIM Query Command..."));

        // Set up a CPIN command to be sent
        // Note that we don't care what the return value is, so long as it's not that the radio isn't ready
        if (!QueueInternalCmd(COMMAND_PORT, this, "AT+CPIN?\r", CMDOPT_SIMQUERY, apiid, 0, 0, 0, ParseGetPhoneLockedState))
        {
            goto SimReadyPhase2;
        }

        // Now, block until we get a response back
        WaitForSingleObject(m_hSimReadyEvent, apiiInfo.dwTimeout);

        // Sleep a bit before the next try
        if (!m_fSimReady && !m_fQuitReadyStateThread)
        {
            Sleep(2000);
            if(dwSimReadyRetry++ > 20)break;
        }
    }
    while (!m_fSimReady && !m_fQuitReadyStateThread);

    if (m_fQuitReadyStateThread)
    {
        goto Error;
    }

SimReadyPhase2:

    // We do not need a second SIM ready phase. On other modules than Siemens there is a
    // need for a RIL_READYSTATE_SMS state, but we are immediately ready for SMS.

Error:
    if (m_hSimReadyEvent)
    {
        CloseHandle(m_hSimReadyEvent);
        m_hSimReadyEvent = NULL;
    }
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("SIMReadyThread : Exiting..."));

    return 0;
}

//
// Launch a thread to see if the SIM is ready or not
//
void CRilHandle::StartReadyStateQuery()
{
    m_fQuitReadyStateThread = FALSE;
    HANDLE hThread = CreateThread(NULL, 0, SimReadyThreadProc, (LPVOID)this, 0, NULL);
    if (NULL != hThread)
        CloseHandle(hThread);   // close handle we don't need
}

void CRilHandle::StopReadyStateQuery()
{
    m_fQuitReadyStateThread = TRUE;
    if (m_hSimReadyEvent)
    {
        SetEvent(m_hSimReadyEvent);
    }
}

//
// Start initialization
//
BOOL CRilHandle::StartInit()
{
    // // TBD_FUNCTION(CRilHandle::StartInit);

    SYNCBLOCK(m_cs);

    DWORD i;
    DWORD dwTemp;
    BOOL fRet = FALSE;
    DWORD dwVersion;
    DWORD dwRHAVersion;
    DWORD fCBM;
    TCHAR tszTmp[MAX_CBM_STRING];

#ifdef WANT_FILE_RILDRV_OUTPUT
    if(GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("DisableDebugFile"), &dwTemp))
    {
        g_dwDisableDebugFile = dwTemp;
    }

    RILRetailTrace((TEXT("g_dwDisableDebugFile = %d \n"), g_dwDisableDebugFile));

#endif

    if(GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("EnableGPRSAttach"), &dwTemp))
    {
        g_dwEnableGPRSAttach = dwTemp;
    }
	g_dwEnableGPRSAttach = 1;
	
    if(GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("EnableSNFASetting"), &dwTemp))
    {
        g_dwEnableSNFASetting = dwTemp;
    }

    RILRetailTrace((TEXT("g_dwEnableGPRSAttach = %d \n"), g_dwEnableGPRSAttach));    
    RILRetailTrace((TEXT("g_dwEnableSNFASetting = %d \n"), g_dwEnableSNFASetting));    
        
    if (m_fInited)
    {
        fRet = TRUE;
        goto Error;
    }

    // Read parameters from the registry
    g_TimeoutCmdInit = TIMEOUT_CMD_INIT;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("TimeoutCmdInit"), &dwTemp))
    {
        g_TimeoutCmdInit = dwTemp;
    }

    g_TimeoutAPIDefault = TIMEOUT_API_DEFAULT;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("TimeoutAPIDefault"), &dwTemp))
    {
        g_TimeoutAPIDefault = dwTemp;
    }

    RILRetailTrace((TEXT("g_TimeoutCmdInit = %d \n"), g_TimeoutCmdInit));    
    RILRetailTrace((TEXT("g_TimeoutAPIDefault = %d \n"), g_TimeoutAPIDefault));    

       
    g_fReceiveCellBroadCast = FALSE;
    g_szReceiveCellBroadCastChannels[0] = '\0';
    g_szReceiveCellBroadCastDCS[0] = '\0';
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EnableCBM"), &fCBM) && fCBM==1)
    {
        g_fReceiveCellBroadCast = TRUE;
    }
    GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EnableCBMChannels"), tszTmp, MAX_CBM_STRING);
    (void)_snprintfz(g_szReceiveCellBroadCastChannels, MAX_CBM_STRING, "%S", tszTmp);
    GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EnableCBMDCS"), tszTmp, MAX_CBM_STRING);
    (void)_snprintfz(g_szReceiveCellBroadCastDCS, MAX_CBM_STRING, "%S", tszTmp);
	

    RILRetailTrace((TEXT("g_fReceiveCellBroadCast = %d \n"), g_fReceiveCellBroadCast));    
    RILRetailTrace((TEXT("g_szReceiveCellBroadCastChannels = %s \n"), g_szReceiveCellBroadCastChannels));    
    RILRetailTrace((TEXT("g_szReceiveCellBroadCastDCS = %s \n"), g_szReceiveCellBroadCastDCS));    

    // Create required events
    m_hInitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hInitEvent) {
        goto Error;
    }

    // Create data event
    m_hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!m_hDataEvent)
    {
        goto Error;
    }

    RILRetailTrace((TEXT("NUMBER_OF_PORTS = %d \n"), NUMBER_OF_PORTS));    
    
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
        InitializeCriticalSection(&(m_csBlockCmds[i]));
        m_hBlockCmdsEvent[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
        if (!m_hBlockCmdsEvent[i]) {
            goto Error;
        }
        m_hBlockRspsEvent[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
        if (!m_hBlockRspsEvent[i]) {
            goto Error;
        }
    }

    m_pInstances = new CDblList<CRilInstanceHandle>;
    if (!m_pInstances) {
        goto Error;
    }

    // Create the serial ports (cmd port and data port (...))
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      m_pComDevice[i] = g_ComManager.CreatePortDevice((COM_PORT_TYPE)i);
      if (!m_pComDevice[i])
      {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("CRilHandle::StartInit : Couldn't open VSP"));
        goto Error;
      }
      TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("CRilHandle::StartInit : VSP is opened successfully"));
    }

    // Launch the auxiliary threads
    if (!LaunchThreads())
    {
        goto Error;
    }

    GetAudioConfig();   // Get the audio config from the registry

    // Initialize some global variables
    g_fReportLocationUpdateAsRegistrationState = FALSE;
    g_bCINDIsActive = FALSE;
    g_iLastRSSI = 99;
    g_iLastSignal = 99;
    g_dwLastSMSCmd = 0;
    for (i=0; i<NUMBER_OF_PORTS; i++)
        m_fUSSDAnswerMode[i] = FALSE;

    //GeneralInitDone(TRUE);
    m_fInited = TRUE;    //Set the init flag to true.

    m_fAllowData = FALSE; // Do not allow any data connections yet
    // Get the RIL version
#ifdef TBD_DEBUG
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("*****************************************"));
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RIL driver version : %a"), RIL_VERSION_STRING);
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Built on %s, at %s"), TString(__DATE__), TString(__TIME__));
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Copyright (c)2003 Siemens AG"));
#endif

    // Get the RHA version
    RHAFunctionList.pfnRHA_GetRHAVersion(&dwRHAVersion);
#ifdef TBD_DEBUG
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RHA version : %d.%d.%d.%d"),
        (dwRHAVersion>>24)&0xFF, (dwRHAVersion>>16)&0xFF, (dwRHAVersion>>8)&0xFF, dwRHAVersion&0xFF);
#endif

    // Get the AudProf version
    AudProf_GetProfilesVersion(&dwVersion);
#ifdef TBD_DEBUG
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Audio profiles version : %d.%d.%d.%d"),
        (dwVersion>>24)&0xFF, (dwVersion>>16)&0xFF, (dwVersion>>8)&0xFF, dwVersion&0xFF);
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("*****************************************\r\n"));
#endif

    // Register the callback function
    fRet = TRUE;

    Error:
    if (!fRet) {
      for (i = 0; i < NUMBER_OF_PORTS; i++) {
        if (m_pComDevice[i]) {
          g_ComManager.DestroyPortDevice((COM_PORT_TYPE)i);
          m_pComDevice[i] = NULL;
        }
        if (m_hBlockCmdsEvent[i]) {
          CloseHandle(m_hBlockCmdsEvent[i]);
          m_hBlockCmdsEvent[i] = NULL;
        }
        if (m_hBlockRspsEvent[i]) {
          CloseHandle(m_hBlockRspsEvent[i]);
          m_hBlockRspsEvent[i] = NULL;
        }
      }

      if (m_hInitEvent) {
        CloseHandle(m_hInitEvent);
        m_hInitEvent = NULL;
      }

      if (m_hDataEvent)
      {
          (void)CloseHandle(m_hDataEvent);
          m_hDataEvent = NULL;
      }
      
    }
    return fRet;
}


void CRilHandle::GeneralInitDone(BOOL bSucceeded)
{
    if (bSucceeded)
    {
        m_fInited = TRUE;
    }
    else
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::FinishInit : Module failed initialization!"));
        m_fFailedInit = TRUE;
    }

    (void)SetEvent(m_hInitEvent);

    // Now we need to send a notification that we've intiailized properly
    SendReadyStateNotification(RIL_READYSTATE_INITIALIZED, TRUE);
}

//
// Finish intialization
//
void CRilHandle::FinishInit(CCommand* pInitCmd, BOOL bSucceeded)
{
    // TBD_FUNCTION(CRilHandle::FinishInit);

    TBD_ASSERT(NULL != pInitCmd);

    if (pInitCmd->FGeneralInit())
    {
        GeneralInitDone(bSucceeded);
    }

    // Allow the module to be turned off from here on
    g_PowerManager.UnlockPowerState();
}


//
// Function passed to CQueue::Enum() below
//
BOOL GenerateCancelledRsp(void* pItem, DWORD dwData)
{
    TBD_FUNCTION(GenerateCancelledRsp);
    TBD_ASSERT(pItem != NULL);
    TBD_ASSERT(dwData != NULL);

    CCommand* pCmd = (CCommand*)pItem;
    CRilHandle* pRilDevice = (CRilHandle*)dwData;
    HRESULT dwError = RIL_E_CANCELLED;

    // Send out cancelled response as a notification
    pCmd->SendResponse(RIL_RESULT_ERROR, &dwError, sizeof(HRESULT));

    // Continue enumeration
    return FALSE;
}


//
//
//
void CRilHandle::PrepareForReinit()
{
    // TBD_FUNCTION(CRilHandle::PrepareForReinit);

    (void)ResetEvent(m_hInitEvent);
    m_fInited = FALSE;
    m_fFailedInit = FALSE;

    // Reset g_dwReadyState
    SendReadyStateNotification(RIL_READYSTATE_NONE, FALSE);

    // Clear the command queue, sending out "cancelled" responses to response callbacks
    // NOTE: The following line has been commented out to prevent commands
    // from being cancelled on start up when the shell turns the radio on.
    //g_pCmdQ[COMMAND_PORT]->Enum(GenerateCancelledRsp, (DWORD)this, TRUE);
}

//
// Launch auxiliary threads
//
BOOL CRilHandle::LaunchThreads()
{
    // TBD_FUNCTION(CRilHandle::LaunchThreads);

    BOOL fRet = FALSE;
    DWORD i;
    HANDLE hNotifications = NULL;
    MSGQUEUEOPTIONS msgOptions = {0};


    // Create a cancel event
    m_hCancelEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hCancelEvent)
    {
        goto Error;
    }

    // Create a hSIMUnlockEvent event
    g_hSIMUnlockEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!g_hSIMUnlockEvent)
    {
        goto Error;
    }
    // Create a g_hAckSMSEvent event
    g_hAckSMSEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!g_hAckSMSEvent)
    {
        goto Error;
    }
    // Create command and response queues
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      m_pComDevice[i]->SetExternalCancelEvent(m_hCancelEvent);
      g_pCmdQ[i] = new CPriorityQueue<CCommand, COMMAND_QUEUE_SIZE>;
      if (!g_pCmdQ[i] || !g_pCmdQ[i]->Init(m_hCancelEvent)) {
        goto Error;
      }
      g_pRspQ[i] = new CQueue<CResponse, RESPONSE_QUEUE_SIZE>;
      if (!g_pRspQ[i] || !g_pRspQ[i]->Init(m_hCancelEvent)) {
        goto Error;
      }
      InitializeCriticalSection(&g_QBlockingData[i].csLock);
      g_QBlockingData[i].hEvents[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
      g_QBlockingData[i].hEvents[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
      if (!g_QBlockingData[i].hEvents[0] || !g_QBlockingData[i].hEvents[1]) {
        goto Error;
      }
    }

    g_pSimLockedQueue = new CQueue<CCommand, 10>;
    if (!g_pSimLockedQueue || !g_pSimLockedQueue->Init(m_hCancelEvent))
    {
        goto Error;
    }

    // Create a thread checkpoint for thread synchronization
    m_pCheckPoint = new CCheckPoint;
    if (!m_pCheckPoint || !m_pCheckPoint->Init(NUMBER_OF_PORTS * 2))
    {
        goto Error;
    }

    // Launch auxiliary threads
    QUEUE_THREAD_DATA* pData;
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      pData = new QUEUE_THREAD_DATA;
      if (!pData) {
        goto Error;
      }
      pData->pRilDevice = this;
      pData->PortType = (COM_PORT_TYPE)i;
      m_hCmdThread[i] = CreateThread(NULL, 0, CmdThreadProc, (LPVOID)pData, 0, &m_dwCmdThreadID[i]);
      if (!m_hCmdThread[i]) {
        delete pData;
        goto Error;
      }
      pData = new QUEUE_THREAD_DATA;
      if (!pData) {
        goto Error;
      }
      pData->pRilDevice = this;
      pData->PortType = (COM_PORT_TYPE)i;
      m_hReadThread[i] = CreateThread(NULL, 0, ReadThreadProc, (LPVOID)pData, 0, &m_dwReadThreadID[i]);
      if (!m_hReadThread[i]) {
        delete pData;
        goto Error;
      }
    }

    g_hSIMUnlockThread = CreateThread(NULL, 0, SIMUnlockThreadProc, (LPVOID)this, 0, NULL);
    if (!g_hSIMUnlockThread)
    {
        goto Error;
    }

    g_hAckSMSThread = CreateThread(NULL, 0, AckSMSThreadProc, (LPVOID)this, 0, NULL);
    if (!g_hAckSMSThread)
    {
        goto Error;
    }    

    // Wait until all threads reach the checkpoint
    if (!m_pCheckPoint->Wait(10000))
    {
        goto Error;
    }

    TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("CRilHandle::LaunchThreads : Launched threads reached ckeckpoint"));
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        for (i = 0; i < NUMBER_OF_PORTS; i++) {
          if (m_hCmdThread[i]) {
            (void)CloseHandle(m_hCmdThread[i]);
            m_hCmdThread[i] = NULL;
            m_dwCmdThreadID[i] = 0;
          }
          if (m_hReadThread[i]) {
            (void)CloseHandle(m_hReadThread[i]);
            m_hReadThread[i] = NULL;
            m_dwReadThreadID[i] = 0;
          }
        }

        if (g_hAckSMSEvent)
        {
            (void)CloseHandle(g_hAckSMSEvent);
            g_hAckSMSEvent = NULL;
        }
        if (g_hSIMUnlockEvent)
        {
            (void)CloseHandle(g_hSIMUnlockEvent);
            g_hSIMUnlockEvent = NULL;
        }
        if (g_hSIMUnlockThread)
        {
            (void)CloseHandle(g_hSIMUnlockThread);
            g_hSIMUnlockThread = NULL;
        }
        delete m_pCheckPoint;
        m_pCheckPoint = NULL;

        for (i = 0; i < NUMBER_OF_PORTS; i ++) {
          if (g_QBlockingData[i].hEvents[0]) {
            CloseHandle(g_QBlockingData[i].hEvents[0]);
            g_QBlockingData[i].hEvents[0] = NULL;
          }
          if (g_QBlockingData[i].hEvents[1]) {
            CloseHandle(g_QBlockingData[i].hEvents[1]);
            g_QBlockingData[i].hEvents[1] = NULL;
          }
          DeleteCriticalSection(&g_QBlockingData[i].csLock);
          delete g_pCmdQ[i];
          delete g_pRspQ[i];
          g_pCmdQ[i] = NULL;
          g_pRspQ[i] = NULL;
        }
        delete g_pSimLockedQueue;
        g_pSimLockedQueue = NULL;

        if (m_hCancelEvent)
        {
            (void)CloseHandle(m_hCancelEvent);
            m_hCancelEvent = NULL;
        }
    }
    return fRet;
}

DWORD CRilHandle::GetInitSMSTries()
{
    DWORD dwSIMTries = 0;

    GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("InitSIMTries"), &dwSIMTries);
    if (dwSIMTries == 0)
        dwSIMTries = 30;

    return dwSIMTries;
}

static HRESULT ParseSIMStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseSIMStatus);
    UINT nValue;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>^SIND: simstatus,<mode>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "^SIND: simstatus,", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    g_iCurrentSIMStatus = nValue;

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

void CRilHandle::SignalInternalSIMReady()
{
    if (m_hInternalSimReadyEvent != NULL)
        SetEvent(m_hInternalSimReadyEvent);
}

void CRilHandle::SignalAbortInternalSIMReady()
{
    if (m_hAbortInternalSimReadyEvent != NULL)
        SetEvent(m_hAbortInternalSimReadyEvent);
}

BOOL CRilHandle::WaitForSIMInit()
{
    BOOL bOK = FALSE;

    g_iCurrentSIMStatus = 0;    
    m_hInternalSimReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hInternalSimReadyEvent) {
        goto Error;
    }
    m_hAbortInternalSimReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hAbortInternalSimReadyEvent) {
        goto Error;
    }
    
    if (!QueueInternalCmdBlocking(URC_PORT, this, "AT^SIND=simstatus,1\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0, ParseSIMStatus))
        goto Error;

    if (g_iCurrentSIMStatus != 5)
    {
        DWORD dwWait;
        HANDLE rghEvents[] = { m_hInternalSimReadyEvent, m_hAbortInternalSimReadyEvent, m_hCancelEvent };

        dwWait = WaitForMultipleObjects(3, rghEvents, FALSE, SIMINIT_TIMEOUT*10);
        if (dwWait != (WAIT_OBJECT_0 + 1) && dwWait != (WAIT_OBJECT_0 + 2))
            bOK = TRUE;
    }
    else
    {
        bOK = TRUE;
    }
Error:
    
    if (m_hInternalSimReadyEvent != NULL)
    {
        CloseHandle(m_hInternalSimReadyEvent);
        m_hInternalSimReadyEvent = NULL;
    }
    if (m_hAbortInternalSimReadyEvent != NULL)
    {
        CloseHandle(m_hAbortInternalSimReadyEvent);
        m_hAbortInternalSimReadyEvent = NULL;
    }
    return bOK;        
}

DWORD WINAPI CRilHandle::CheckPPLMNThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    if (IS_ADI)
    {
        // Problem: we have to wait for ther module to leave the +CREG: 2 state, before we can
        // try any PPLMN command. If the module has been shutdown before that happens, this
        // thread must be aborted.
        while (g_dwRegStatus == RIL_REGSTAT_ATTEMPTING && g_PowerManager.GetEquipmentState() == RIL_EQSTATE_FULL)
            Sleep(100);
        if (g_PowerManager.GetEquipmentState() == RIL_EQSTATE_FULL)
            QueueInternalCmd(COMMAND_PORT, pHandle, "AT^SPLR=?\r", CMDOPT_NONE, APIID_GETPREFERREDOPERATORLIST, 0, GetInitSMSTries(), 1000, ParseGetPreferredOperatorListSize);
    }
    else
    {
        QueueInternalCmd(COMMAND_PORT, pHandle, "AT^SPLR=?\r", CMDOPT_NONE, APIID_GETPREFERREDOPERATORLIST, 0, 2, 7500, ParseGetPreferredOperatorListSize);
    }
    return 0;
}

//
//
//
DWORD CRilHandle::SIMUnlockThread()
{
    char szCmd[MAX_ATCMD_LEN];
    CNotificationData* pnd = NULL;
    DWORD fCOLP, fAoC, fTW, fNoBlackList;
    DWORD dwWaitForCSMS;
    DWORD dwTries, dwDelay;
    DWORD dwSIMTries;

    while (1)
    {
        m_fSMSURCInitPhase = FALSE;

        WaitForSingleObject(g_hSIMUnlockEvent, INFINITE);

        // See if the thread needs to terminate
        if (FCancelSet())
        {
            break;
        }

#ifdef RIL_ENABLE_ALS
        g_dwALSLine = 0;
#endif
        ClearFDPBCache();
        dwSIMTries = GetInitSMSTries();
        m_szLastDialedNumber[0] = '\0';

        m_fSimulatedCLIPActive = TRUE;          // Applies only for MC75

        g_dwPreferredOperatorListStart = 0;
        Sleep(1000);    // Allow the SIM to initialize
        if (IS_ADI)
        {
            // Usually AT+CMER is activated later during this initialization, but for the MC75 and TC63
            // we need it do determine, if the SIM is ready now.
            if (!QueueInternalCmdBlocking(URC_PORT, this, "AT+CMER=2,0,0,2\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, dwSIMTries, 1000))
                continue;

            // We can only use the simstatus URC on MC75 Release3, TC63 Release2 or on MC75 Release2 if the 4th multiplexer port is disabled.
            if ((g_dwModemType==MODEMTYPE_SIEMENS_MC75 && g_dwModemVersion==2001 && g_ComManager.GetAuxDataPort()==INVALID_PORT) 
                || (g_dwModemType==MODEMTYPE_SIEMENS_MC75 && g_dwModemVersion>=2100)
                || (g_dwModemType==MODEMTYPE_SIEMENS_TC63 && g_dwModemVersion>=2000))
            {
                // We now wait for the URC indicating, that the SIM is ready now before we proceed with the next step.
                if (!WaitForSIMInit())
                    continue;
            }
        }

        if (!QueueInternalCmdBlocking(DATA_PORT, this, "AT+CNMI=0,0,0,0\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
            continue;
#ifdef AUXCOM_PORT
        if (!QueueInternalCmdBlocking(COMMAND_PORT, this, "AT+CNMI=0,0,0,0\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
            continue;
#endif

        // It can not control SMS storage         
        // Before intializing anything concerning the SMS storage we have to
        /*
        if (!GetComDevice(DATA_PORT)->FDataMode()) {
            QueueInternalCmdBlocking(DATA_PORT, this, SMS_INIT_STORAGE_ORDER, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, dwSIMTries, 1000);
        }
        else {
            QueueInternalCmdBlocking(COMMAND_PORT, this, SMS_INIT_STORAGE_ORDER, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, dwSIMTries, 1000);
        }
        */

        // First we initialize the SMS Phase 2 notifications. This prevents us from missing an SMS
        // which comes in during initialisation. It is also a fallback in case anything goes wrong
        // with the activation of SMS Phase 2+. Additionally we route all SMS to the SIM, to prevent
        // a missleading assignment of incomming SMS in case the SIM is changed.
        if (IS_ADI && !GetComDevice(DATA_PORT)->FDataMode()) {
            // Problem: the MC75 sometime takes a very long time to initialize. To prevent the
            // command port to be blocked for this time, we first try to perform the first command
            // on the data port.
            QueueInternalCmdBlocking(DATA_PORT, this, SMS_INIT_PREFERRED_STORE, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, dwSIMTries, 1000);
        }
#ifdef AUXCOM_PORT
        if (!QueueInternalCmd(COMMAND_PORT, this, SMS_INIT_PREFERRED_STORE, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, dwSIMTries, 1000))
            continue;
#endif
        if (!QueueInternalCmdBlocking(URC_PORT, this, SMS_INIT_PREFERRED_STORE, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, dwSIMTries, 1000))
            continue;
        // SMS-URC initilaization phase: in case we go to suspend during this phase we turn on the URC for
        // SMS in order to wake up on incomming SMS, but we don't report the incomming SMS to the OS,
        // yet. Instead we check for SMS on SIM or ME memory at the end of the initialization phase.
        m_fSMSURCInitPhase = TRUE;
        if (!QueueInternalCmdBlocking(URC_PORT, this, SMS_PHASE2_CNMI, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 2, 1000))
            continue;

        strcpy(szCmd, "AT+CSMS=1\r");
        // Please notice: these values are also later used for AT^SPLR=? command.
        if (IS_ADI)
        {
            // The MC75 sometimes takes some time to initialize the SMS phase 2+ correctly.
            dwWaitForCSMS = 60000;
            dwTries = dwSIMTries;
            dwDelay = 1000;
        }
        else
        {
            dwWaitForCSMS = 10000;
            dwTries = 0;    // The MC4x, MC5x and AC45 don't access the SIM for the AT+CSMS command, so wie don't need dwSIMTries here.
            dwDelay = 0;
        }

        // Place the command into the command queue and wait for response.
        if (!QueueInternalCmdBlocking(COMMAND_PORT, this, szCmd, CMDOPT_NONE, APIID_UNLOCKPHONE, dwWaitForCSMS, dwTries, dwDelay))
            continue;

#ifdef AUXCOM_PORT
        // Workaround: this SMS-Phase must be set on all channels used for SMS set to the same value.
        if (!QueueInternalCmdBlocking(URC_PORT, this, szCmd, CMDOPT_NONE, APIID_UNLOCKPHONE, dwWaitForCSMS, dwTries, dwDelay))
            continue;
#endif

        // Send the unlocked init strings
        if (!g_ComManager.SendUnlockedInitCmds())
            continue;

        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EnableAdviceOfCharge"), &fAoC) && (fAoC>=0 && fAoC<=1)) {
            _snprintfz(szCmd, 20, "AT^SACM=%u\r", fAoC);
            if (!QueueInternalCmdBlocking(URC_PORT, this, szCmd, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
                continue;
        }
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EnableTemperatureWarning"), &fTW) && (fTW>=0 && fTW<=1)) {
            _snprintfz(szCmd, 20, "AT^SCTM=%u\r", fTW);
            if (!QueueInternalCmdBlocking(URC_PORT, this, szCmd, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
                continue;
        }
#ifdef RIL_ENABLE_ALS
        {
            _snprintfz(szCmd, 20, "AT^SALS=1,1\r");
            if (!QueueInternalCmdBlocking(URC_PORT, this, szCmd, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
                continue;
        }
#endif
        g_fDisableCOLPInformation = TRUE;
        if (g_dwModemType != MODEMTYPE_SIEMENS_MC45 && g_dwModemType != MODEMTYPE_SIEMENS_MC46) {
            g_fDisableCOLPInformation = FALSE;
            g_nCOLPStatus = 1;
            if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisableCOLPInformation"), &fCOLP) && (fCOLP>=0 && fCOLP<=1)) {
                // Disable COLP information is needed, when the radiotest is run on a network, which modifies
                // the format of the dialed number in the COLP notification. The radiometrics.exe will not
                // recognize, that the number dialed and the number shown are identical and sets the test
                // to failed although everything was correct.
                if (fCOLP == 1) {
                    g_nCOLPStatus = 0;
                    g_fDisableCOLPInformation = TRUE;
                    if (!QueueInternalCmdBlocking(COMMAND_PORT, this, "AT+COLP=0\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
                        continue;
                }
            }
        }
#ifdef SIEMENS_MAGNETO
        _snprintfz(szCmd, 20, "AT+CSSN=1,1\r");
        if (!QueueInternalCmdBlocking(URC_PORT, this, szCmd, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
            continue;
#endif
        if (IS_ADI) {
            // New handling of the blacklist in the MC75: this feature has to be configured at startup.
            if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoBlackList"), &fNoBlackList) && (fNoBlackList==0 || fNoBlackList==1)) {
                if (!QueueInternalCmdBlocking(COMMAND_PORT, this , fNoBlackList==0?"AT^SCFG=\"Call/Blacklist\",\"0031\"\r":"AT^SCFG=\"Call/Blacklist\",\"0030\"\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
                    continue;
            }
        }
        Sleep(1000);

        SendReadyStateNotification(RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS, TRUE);

        if (g_iSetCOPSinInit != NO_INIT_COPS_SETTING)
        {
            // Usually the MMI sends its request to set the registration mode when the module hasn't started yet. For
            // this reason the value is stored to be set later during the init sequence (which is here).

            // Query the current +COPS state for further use. The result will be stored in g_iLastCOPS.
            QueueInternalCmdBlocking(COMMAND_PORT, this, "AT+COPS?\r", CMDOPT_NONE, APIID_GETCURRENTOPERATOR, 0, 0, 0, ParseGetCurrentOperator);
            if (g_iSetCOPSinInit != g_iLastCOPS || (g_iSetCOPSinInit != 0 && g_iSetCOPSinInit == 2))
            {
                if (g_iSetCOPSinInit == 0 || g_iSetCOPSinInit == 2)
                    _snprintfz(szCmd, MAX_ATCMD_LEN, "AT+COPS=%u\r", g_iSetCOPSinInit);
                else
                    _snprintfz(szCmd, MAX_ATCMD_LEN, "AT+COPS=%u,2,%s\r", g_iSetCOPSinInit, g_szSetCOPSinInitOperator);
                QueueInternalCmd(COMMAND_PORT, this, szCmd, CMDOPT_NONE, APIID_REGISTERONNETWORK, 0, 1, 1000);
            }
            g_iSetCOPSinInit = NO_INIT_COPS_SETTING;
        }

        // SMS initialization ended, now the power manager will check if there are missed SMS and turn on the
        // regular SMS notification method.
        m_fSMSURCInitPhase = FALSE;
        if (!g_PowerManager.EnterUrcState(NULL, MODEMSTATE_SIEMENS_POWERON, TRUE))
            continue;

        // Initialize the EONS capability
        if (!m_pEonsHandling->Init(this))
            continue;

        // The FetchOwnNumber() function has got two purposes:
        // 1) Fetch the content of the own numbers phonebook (MC4x only)
        // 2) Makes sure, that the SIM is completely ready (the phonebook is the last part of the SIM to become ready)
        if (!FetchOwnNumber())
            continue;

        SendReadyStateNotification(RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS|RIL_READYSTATE_SIM_PB, TRUE);

        if (g_dwModemType != MODEMTYPE_SIEMENS_MC55 && g_dwModemType != MODEMTYPE_SIEMENS_MC56) {
            // The MC5x has got the AT+CPOL command, so we don't have to use AT^SPLR. The MC75 also supports this
            // command, but we need the AT^SPLR=? values for other reasons, so cannot ommit this step in this case.
            CreateThread(NULL, 0, CheckPPLMNThreadProc, (LPVOID)this, 0, 0);
        }

        m_fAllowData = TRUE; // We can now allow data connections

#if defined(RIL_SYSTEMTYPE_EDGE)
        g_dwLastEDGEStatus = 0;
        if (g_dwModemType == MODEMTYPE_SIEMENS_MC75)    // EDGE or GPRS?
            CreateThread(NULL, 0, CheckEDGEStatusThreadProc, (LPVOID)this, 0, 0);
#endif

        if(g_dwEnableGPRSAttach == 1)
        {
            CreateThread(NULL, 0, GPRSReAttachThreadProc, (LPVOID)this , 0, 0);
        }

        // Enable the SIM-Toolkit handling if required
        if (!m_pSimToolkitHandling->Init(this))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, (TEXT("SIMUnlockThread : Unable to initialize SIM-Toolkit handling")));
            continue;
        }
    }


    return 0;
}

DWORD WINAPI SIMUnlockThreadProc(LPVOID lpParameter)
{
    TBD_FUNCTION(SIMUnlockThreadProc);
    TBD_ASSERT(lpParameter != NULL);
    CRilHandle* pRilDevice = (CRilHandle*)lpParameter;

    if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, (TEXT("SIMUnlockThreadProc : Unable to raise priority of read thread!!")));
        return 0;
    }

    TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("SIMUnlockThreadProc : Entering, pRilDevice=0x%x"),pRilDevice);
    const DWORD dwReturn = pRilDevice->SIMUnlockThread();
    TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("SIMUnlockThreadProc : THREAD IS EXITING, dwReturn=0x%x"),dwReturn);
    return dwReturn;
}


//
// Test the cancel event
//
BOOL CRilHandle::FCancelSet() const
{
    // // TBD_FUNCTION(CRilHandle::FCancelSet);
    return (WAIT_OBJECT_0 == WaitForSingleObject(m_hCancelEvent, 0));
}


//
//
//
BOOL CRilHandle::WaitForInit(DWORD dwTimeout) const
{
    // TBD_FUNCTION(CRilHandle::WaitForInit);
    HANDLE rghEvents[] = { m_hInitEvent, m_hCancelEvent };
    switch (WaitForMultipleObjects(2, rghEvents, FALSE, dwTimeout))
    {
    case WAIT_OBJECT_0:
    case WAIT_OBJECT_0 + 1:
        return TRUE;
    default:
        return FALSE;
    }
}


//
// Simply wait until command handling is unblocked or cancelled.
// Returns false if the command thread should quit.
//
BOOL CRilHandle::WaitForCommandOrCancel(COM_PORT_TYPE PortType)
{
  BOOL    fCmd = FALSE;
  HANDLE  rghEvents[] = { g_pCmdQ[PortType]->GetPutEvent(), m_hCancelEvent };
  HANDLE  rghBlockEvents[] = { m_hBlockCmdsEvent[PortType], m_hCancelEvent };

  while (!fCmd) {
    // Wait until command handling is unblocked or we're cancelled
    EnterCriticalSection(&(m_csBlockCmds[PortType]));
    if (WaitForMultipleObjects(2, rghBlockEvents, FALSE, INFINITE) != WAIT_OBJECT_0) {
      LeaveCriticalSection(&(m_csBlockCmds[PortType]));
      return FALSE;
    }

    // Lock the command handling in port object
    m_pComDevice[PortType]->LockRILCmd();
    LeaveCriticalSection(&(m_csBlockCmds[PortType]));

    // Wait until command is availabe or we're cancelled
    if (WaitForMultipleObjects(2, rghEvents, FALSE, INFINITE) != WAIT_OBJECT_0) {
      m_pComDevice[PortType]->UnlockRILCmd();
      return FALSE;
    }

    // Check if there is really a command available
    if (!g_pCmdQ[PortType]->FEmpty()) {
      fCmd = TRUE;
    } else {
      // Unlock the command handling in port object again for next wait
      m_pComDevice[PortType]->UnlockRILCmd();
    }
  }

  // Set the put event again which has been reset by the last wait
  SetEvent(g_pCmdQ[PortType]->GetPutEvent());

  return TRUE;
}


//
// Returns true if there is response data available that can be executed now.
// Returns false if the response thread should quit.
//
BOOL CRilHandle::WaitForResponseOrCancel(COM_PORT_TYPE PortType)
{
  DWORD    dwMask;
  DWORD    dwErrors;
  COMSTAT  stat;
  BOOL     fData = FALSE;
  HANDLE   rghBlockEvents[] = { m_hBlockRspsEvent[PortType], m_hCancelEvent };

  // Wait until we have data or we're cancelled
  while (!fData) {
    // Wait until we have a valid COM port
    if (!m_pComDevice[PortType]->WaitForDownstreamPort()) {
      return FALSE;
    }

    // Wait until command handling is unblocked again or we are cancelled
    if (WaitForMultipleObjects(2, rghBlockEvents, FALSE, INFINITE) != WAIT_OBJECT_0) {
      return FALSE;
    }

    // Now reset COM mask and wait until we have data
    m_dwPendingPortCalls[PortType]++;
    if (m_pComDevice[PortType]->VirtSetCommMask(DEFAULT_COM_MASK)) {

      RILRetailTrace((TEXT("RilDrv: WaitForResponseOrCancel() - VirtSetCommMask\r\n")));        
      if (m_pComDevice[PortType]->VirtWaitCommEvent(&dwMask)) {

        RILRetailTrace((TEXT("RilDrv: WaitForResponseOrCancel() - VirtWaitCommEvent, Event = %x\r\n"), dwMask));        

        //m_pComDevice[PortType]->VirtGetCommModemStatus(&dwMask);
        //RILRetailTrace((TEXT("RilDrv: WaitForResponseOrCancel() - VirtGetCommModemStatus, Status = %x\r\n"), dwMask));                
    
        //if (dwMask & EV_RING)
        //{
        //    RILRetailTrace((TEXT("RilDrv: WaitForResponseOrCancel() - VirtWaitCommEvent, EV_RING\r\n")));        
        //}
        
        // Clear possible ERR or BREAK
        if (dwMask & EV_ERR) {
          m_pComDevice[PortType]->VirtClearCommError(&dwErrors, NULL);
        }
        if (dwMask & EV_BREAK) {
          m_pComDevice[PortType]->VirtClearCommBreak();
        }
        // Now check if there is any data
        if ((dwMask & EV_RXCHAR) && (WaitForSingleObject(m_hBlockRspsEvent[PortType], 0) == WAIT_OBJECT_0)) {
          fData = TRUE;
        } else {
          if (m_pComDevice[PortType]->VirtClearCommError(&dwErrors, &stat)) {
            if (stat.cbInQue && (WaitForSingleObject(m_hBlockRspsEvent[PortType], 0) == WAIT_OBJECT_0)) {
              fData = TRUE;
            }
          }
        }
      }
    } else {
      // COM call failed, check if the module is still alive
      if (m_pComDevice[PortType]->IsDownstreamPortOpen()) {
#ifndef SIEMENS_MC45_DSB
        if (!RHAFunctionList.pfnRHA_IsRadioPwrOn()) {
#endif
          RILRetailTrace((TEXT("RilDrv: WaitForResponseOrCancel(%s) - Module powered off externally\r\n"), g_ComManager.GetDebugName(PortType)));
          m_dwPendingPortCalls[PortType]--;
          g_PowerManager.ForcePowerOff();
          Sleep(10);
          continue;
#ifndef SIEMENS_MC45_DSB
        }
#endif
      }
    }
    m_dwPendingPortCalls[PortType]--;
  }

  return TRUE;
}


//
//
//
BOOL CRilHandle::BlockCommandPriority(DWORD dwPriority, BOOL fAbort) {
  DWORD  i;
  BOOL   fRet = TRUE;

  // Abort possibly running commands if required by simply blocking the command handling
  if (fAbort) {
    if (!BlockCommandHandling()) {
      fRet = FALSE;
    }
  }

  // Now block all commands below the given priority
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    if (!g_pCmdQ[i]->SetPriorityLock(dwPriority)) {
      fRet = FALSE;
    }
  }

  // Now unblock command handling again if it was blocked before
  if (fAbort) {
    if (!UnblockCommandHandling()) {
      fRet = FALSE;
    }
  }

  return fRet;
}


//
//
//
BOOL CRilHandle::BlockCommandHandling(COM_PORT_TYPE PortType) {
  // First check if we're not already blocked
  if ((WaitForSingleObject(m_hBlockCmdsEvent[PortType], 0) != WAIT_OBJECT_0) && (WaitForSingleObject(m_hBlockRspsEvent[PortType], 0) != WAIT_OBJECT_0)) {
    return FALSE;
  }

  SYNCBLOCK(m_csBlockCmds[PortType]);

  // Block the command queue
  ResetEvent(m_hBlockCmdsEvent[PortType]);

  // Now abort possible running commands
  SetEvent(g_pCmdQ[PortType]->GetPutEvent());
  m_pComDevice[PortType]->AbortCommand();

  // And block the response queue
  ResetEvent(m_hBlockRspsEvent[PortType]);

  // Abort possible WaitCommEvent() in response queue
  if (!m_pComDevice[PortType]->FDataMode()) {
    m_pComDevice[PortType]->VirtSetCommMask(0);
  }

  // Finally wait until all pending COM calls has finished properly
  for (DWORD i = 0; (i < 100) && m_dwPendingPortCalls[PortType]; i++) {
    Sleep(100);
  }
  m_dwPendingPortCalls[PortType] = 0;

  // Wait until possibly running command has finished
  m_pComDevice[PortType]->LockRILCmd();
  m_pComDevice[PortType]->UnlockRILCmd();

  return TRUE;
}


//
//
//
BOOL CRilHandle::BlockCommandHandling() {
DWORD  i;
BOOL   fRet = TRUE;

  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    if (!BlockCommandHandling(i)) {
      fRet = FALSE;
    }
  }
  return fRet;
}


//
//
//
BOOL CRilHandle::UnblockCommandHandling(COM_PORT_TYPE PortType) {
  // First check if we're not already unblocked
  if ((WaitForSingleObject(m_hBlockCmdsEvent[PortType], 0) == WAIT_OBJECT_0) && (WaitForSingleObject(m_hBlockRspsEvent[PortType], 0) == WAIT_OBJECT_0)) {
    return FALSE;
  }

  // Unblock the response queues
  SetEvent(m_hBlockRspsEvent[PortType]);

  // Unblock the command queues
  SetEvent(m_hBlockCmdsEvent[PortType]);

  // Restart possibly locked queue handling
  SetEvent(g_pCmdQ[PortType]->GetPutEvent());

  return TRUE;
}


//
//
//
BOOL CRilHandle::UnblockCommandHandling() {
DWORD  i;
BOOL   fRet = TRUE;

  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    if (!UnblockCommandHandling(i)) {
      fRet = FALSE;
    }
  }
  return fRet;
}


//
//
//
BOOL CRilHandle::AddToList(CRilInstanceHandle* const pInstance)
{
    // TBD_FUNCTION(CRilHandle::AddToList);
    return m_pInstances->Add(pInstance);
}


//
//
//
BOOL CRilHandle::RemoveFromList(CRilInstanceHandle* const pInstance)
{
    // TBD_FUNCTION(CRilHandle::RemoveFromList);
    return m_pInstances->Remove(pInstance);
}


//
//
//
HRESULT CRilHandle::GetNextCmdID()
{
    // TBD_FUNCTION(CRilHandle::GetNextCmdID);
    SYNCBLOCK(m_csCmdID);

    // Make sure not to wrap into error values
    if (FAILED(m_hrNextCmdID))
    {
        m_hrNextCmdID = INITIAL_COMMAND_ID;
    }

    return m_hrNextCmdID++;
}


//
// Function passed to CDlbList::Enum() below
//
BOOL NotifyHandle(void* pItem, DWORD dwData)
{
    //TBD_FUNCTION(NotifyHandle);
    TBD_ASSERT(pItem != NULL);
    TBD_ASSERT(dwData != NULL);

    CRilInstanceHandle* pHandle = (CRilInstanceHandle*)pItem;
    CNotificationData* pnd = (CNotificationData*)dwData;

    if (pHandle->FReadyForNotifications())
    {
        pHandle->Notify(pnd->GetCode(), S_OK, pnd->GetBlob(), pnd->GetSize());
    }

    // Continue enumeration
    return FALSE;
}


//
// Broadcast a notification to all clients
//
void CRilHandle::BroadcastNotification(CNotificationData*& rpnd)
{
    // TBD_FUNCTION(CRilHandle::BroadcastNotification);
    SYNCBLOCK(g_csClosing);
    TBD_ASSERT(NULL != m_pInstances);
    TBD_ASSERT(NULL != rpnd);

    TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("CRilHandle::BroadcastNotification : Broadcasting notification 0x%x"), rpnd->GetCode());

    // Notify all RIL proxy instances
    m_pInstances->Enum(NotifyHandle, (DWORD)rpnd);

    // Get rid of the notififcation
    delete rpnd;
    rpnd = NULL;
}


//
//
//
BOOL CRilHandle::BroadcastRealBlobNotification(const DWORD dwNotificationCode, const void* const pBlob,
                                               const UINT cbBlob)
{
    // TBD_FUNCTION(CRilHandle::BroadcastRealBlobNotification);
    CNotificationData* pnd = NULL;
    BOOL fRet = FALSE;

    pnd = new CNotificationData;
    if (!pnd || !pnd->InitFromRealBlob(dwNotificationCode, pBlob, cbBlob))
    {
        goto Error;
    }

    BroadcastNotification(pnd);
    TBD_ASSERT(NULL == pnd);
    fRet = TRUE;

    Error:
    delete pnd;
    return fRet;
}


//
//
//
BOOL CRilHandle::BroadcastDWORDBlobNotification(const DWORD dwNotificationCode, const DWORD dwBlob)
{
    // TBD_FUNCTION(CRilHandle::BroadcastDWORDBlobNotification);
    CNotificationData* pnd = NULL;
    BOOL fRet = FALSE;

    pnd = new CNotificationData;
    if (!pnd || !pnd->InitFromDWORDBlob(dwNotificationCode, dwBlob))
    {
        goto Error;
    }

    BroadcastNotification(pnd);
    TBD_ASSERT(NULL == pnd);
    fRet = TRUE;

    Error:
    delete pnd;
    return fRet;
}

//
//
//
DWORD WINAPI PromptForSimAndRequeueCmdProc(LPVOID lpParameter)
{
    //TBD_FUNCTION(PromptForSimAndRequeueCmdProc);
    HRESULT Result;
    HINSTANCE hInstSimSec;


    // Load the simsec library
    hInstSimSec = LoadLibrary(TEXT("simsec.dll"));
    if (!hInstSimSec)
    {
        goto Exit;
    }

    // Get the address of the simsec ui
    typedef HRESULT (*SIMSECURITYUNLOCKPHONE) (HWND hwndMain, DWORD dwStyle);
    SIMSECURITYUNLOCKPHONE pfnSIMSecurityUnlockPhone;
    pfnSIMSecurityUnlockPhone = (SIMSECURITYUNLOCKPHONE)GetProcAddress(hInstSimSec, TEXT("SIMSecurityUnlockPhone"));
    if (pfnSIMSecurityUnlockPhone)
    {
        // Put up the UI
#ifdef TPC
        Result = pfnSIMSecurityUnlockPhone(NULL, SIMSEC_STYLE_CANNOT_CANCEL | SIMSEC_STYLE_EMERGENCY_CALL | SIMSEC_STYLE_BRANDED);
#else
        Result = pfnSIMSecurityUnlockPhone(NULL, SIMSEC_STYLE_EMERGENCY_CALL);
#endif
    }
    else
    {
#ifdef TPC
        ASSERT(FALSE);
        goto Exit;
#else
        // backward compatibility for old simsecui, which doesn't have UnlockPhone, and instead uses CheckPin
        typedef HRESULT (*SIMSECURITYCHECKPIN) (HWND hwndMain, SIMPIN_TYPE nPinType, DWORD dwStyle);
        SIMSECURITYCHECKPIN pfnSIMSecurityCheckPin;
        pfnSIMSecurityCheckPin = (SIMSECURITYCHECKPIN)GetProcAddress(hInstSimSec, TEXT("SIMSecurityCheckPin"));
        if (pfnSIMSecurityCheckPin)
        {
            // Put up the UI
            Result = pfnSIMSecurityCheckPin(NULL, (SIM_LOCKFACILITY_SIM == g_dwLockFacility) ? SIMPIN_SIM : SIMPIN_SIM2,
                SIMSEC_STYLE_AUTO_CLOSE | SIMSEC_STYLE_EMERGENCY_CALL);
        }
        else
        {
            goto Exit;
        }
#endif // !TPC
    }


    // Free the library
    FreeLibrary(hInstSimSec);

Exit:
    // Not prompting anymore
    g_PromptingForSim=FALSE;

    // Now safe to requeue the pending commands to try again
    // Requeue any existing commands
    // If the SIM was unlocked, they'll now succeed.
    // Otherwise they'll fail as expected.
    CCommand* pCmd;
    while (g_pSimLockedQueue->Get(pCmd, 0)==S_OK)
    {
        if (!g_pCmdQ[COMMAND_PORT]->Put(pCmd, INFINITE))
        {
            delete pCmd;
        }
    }

    return 0;
}

//
//
//
BOOL PromptForSimAndRequeueCmd(CCommand* pCmd, DWORD dwLockFacility)
{
    //TBD_FUNCTION(PromptForSimAndRequeueCmd);
    TBD_ASSERT(NULL != pCmd);

    BOOL fRet=FALSE;

    // Queue command up on SIM retry queue
    if(pCmd->FDial())
    {
        // Not re-queuing dial commands
        // Leave fRet FALSE to indicate that the existing response should be used
    }
    else
    {
        pCmd->ClrCmdOpt(CMDOPT_RETRYONSIMLOCKED);
        fRet = g_pSimLockedQueue->Put(pCmd, 0);
        if (!fRet)
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("PromptForSimAndRequeueCmd : Unable to queue cmd"));
            goto Exit;
        }
    }

    // Is another dialog already up?
    // Note: don't need to worry about
    if (!g_PromptingForSim)
    {
        HANDLE hThread;
        // Remember to save nPinType;
        g_dwLockFacility=dwLockFacility;
        hThread = CreateThread(NULL, 0, PromptForSimAndRequeueCmdProc, (LPVOID)NULL, 0, NULL);
        if (!hThread)
        {
            // Eek, we can't create a thread!
            if(pCmd->FDial())
            {
                // Dial commands were not put on the queue, so we don't need to remove them now
            }
            else
            {
                // By definition, our command must be the only one in the queue. Just get it back out.
                // When we return fRet==FALSE, the calling code will fail the response to the app.
                CCommand *pCmdTmp;
                g_pSimLockedQueue->Get(pCmdTmp, 0);
                TBD_ASSERT(pCmd == pCmdTmp);
            }
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("PromptForSimAndRequeueCmd : Unable to CreateThread(PromptForSimAndRequeueCmdProc)"));
            goto Exit;
        }
        else
        {
            CloseHandle(hThread);   // close handle we don't need
        }
        g_PromptingForSim=TRUE;
    }

    if(!pCmd->FDial())
    {
        // See comment above about leaving fRet FALSE for dial commands
        fRet=TRUE;
    }
Exit:
    return fRet;
}

//
// Handle response to an AT command
//
BOOL CRilHandle::HandleRsp(COM_PORT_TYPE PortType, CCommand*& rpCmd, CResponse*& rpRsp, BOOL& rfHungUp, BOOL& rfRadioOff)
{
    // TBD_FUNCTION(CRilHandle::HandleRsp);
    TBD_ASSERT(rpCmd != NULL);
    TBD_ASSERT(rpRsp != NULL);

    void* pBlob = NULL;
    UINT cbBlob = 0;
    HRESULT hrError;
    CNotificationData* pnd = NULL;
    BOOL fRet = FALSE;

    rfHungUp = FALSE;

    // Handling of the CME-Error translation table.
    if (rpRsp->GetNotifyCode() == RIL_RESULT_ERROR) {
        const CME_TRANSLATION* pCMET;
        void* pBlob;
        UINT cbBlob;

        pCMET = rpCmd->GetCMET();
        rpRsp->GetBlob(pBlob, cbBlob);

        if (pCMET != NULL && pBlob != NULL)
        {
            HRESULT hr = NULL;
            UINT i = 0;

            while (pCMET[i].hrModuleErrorCode != NULL) {
                if (pCMET[i].hrModuleErrorCode == *((HRESULT*)pBlob)) {
                    hr = pCMET[i].hrMicrosoftErrorCode;
                    break;
                }
                i++;
            }
            if (hr != NULL)
                memcpy(pBlob, &hr, sizeof(HRESULT));
        }

    }

    if (rpRsp->GetNotifyCode() == RIL_RESULT_ERROR && rpCmd->IsSMSAck()) {
        // Special problem: when a SMS scknowlegment fails, the +CNMI-settings are
        // automaticalli reset to 0,0,0,0 (no inidications of incomming SMS.
        // For this reason we have to reeactivate the CNMI indications.
        CreateThread(NULL, 0, CNMIReactivationThreadProc, (LPVOID)this, 0, NULL);
    }

    // Handle logging of OK and ERROR responses
    if (NULL == rpCmd->GetParseFunc() && !rpCmd->FNoOp())
    {
        DWORD dwZone = rpCmd->CmdOptIsSet(CMDOPT_POLLING) ?
            RILLOG_ZONE_CMD_POLLING : RILLOG_ZONE_CMD;
        if (RIL_RESULT_OK == rpRsp->GetNotifyCode())
        {
            g_RilLog.LogEvent(dwZone, RILLOG_EVENT_CMDRSPOK);
        }
        else if (E_FAIL == rpRsp->GetError())
        {
            g_RilLog.LogEvent(dwZone, RILLOG_EVENT_CMDRSPERROR);
        }
    }

    // Is this the last modem init command? If so, and if it succeeded, then we're done with initialization
    if (rpCmd->FFinalInit())
    {
        FinishInit(rpCmd,(RIL_RESULT_OK == rpRsp->GetNotifyCode()));
    }

    if ((rpCmd->CmdOptIsSet(CMDOPT_UNLOCKING)) && (RIL_RESULT_OK == rpRsp->GetNotifyCode()) && (!rpCmd->CmdOptIsSet(CMDOPT_CPIN2RELATED)))
    {
        // Well, looks like we successfully unlocked the phone
        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("The phone is now unlocked"));
        SendReadyStateNotification(RIL_READYSTATE_UNLOCKED, TRUE);
        Sleep(100);
        SetEvent(g_hSIMUnlockEvent);
    }

    if ((rpCmd->CmdOptIsSet(CMDOPT_RETRYONSIMLOCKED)) && (rpRsp->GetNotifyCode()==RIL_RESULT_ERROR))
    {
        BOOL bPrompt=FALSE;
        void* pBlobSim = NULL;
        UINT cbBlobSim = 0;
        rpRsp->GetBlob(pBlobSim, cbBlobSim);
        ASSERT(cbBlobSim==sizeof(DWORD));
        DWORD dwLockFacility;
        switch(*(DWORD*)pBlobSim)
        {
        case RIL_E_PHSIMPINREQUIRED:
        case RIL_E_PHFSIMPINREQUIRED:
        case RIL_E_PHFSIMPUKREQUIRED:
        case RIL_E_SIMPINREQUIRED:
        case RIL_E_SIMPUKREQUIRED:
        case RIL_E_SIMNOTINSERTED:
        case RIL_E_SIMFAILURE:
            dwLockFacility=SIM_LOCKFACILITY_SIM;
            bPrompt=TRUE;
            break;
        case RIL_E_SIMPIN2REQUIRED:
        case RIL_E_SIMPUK2REQUIRED:
            dwLockFacility=SIM_LOCKFACILITY_SIM_PIN2;
            bPrompt=TRUE;
        }

        if (bPrompt)
        {
            if (PromptForSimAndRequeueCmd(rpCmd,dwLockFacility))
            {
                delete pnd;
                delete rpRsp;
                rpCmd = NULL;
                rpRsp = NULL;
                return TRUE;
            }
        }
    }

    // If we need to re-init the modem after this command and the command was sucessful, do it
    if (rpCmd->FReinit() && RIL_RESULT_OK == rpRsp->GetNotifyCode()) {
      PrepareForReinit();
      g_dwSIMLockedState = RIL_LOCKEDSTATE_UNKNOWN;
      if (!g_ComManager.SendInitCmds(COM_REINIT_INDEX)) {
        // We failed sending the init strings -- RIL is not initialized
        goto Error;
      }
    }

    if (rpCmd->FHangup())
    {
        // If this is a hangup command and we got a NO CARRIER response, turn it into OK
        if (RIL_RESULT_NOCARRIER == rpRsp->GetNotifyCode())
        {
            rpRsp->NoCarrierToOK();
        }

        // If hangup was successful, let the caller know
        if (RIL_RESULT_OK == rpRsp->GetNotifyCode())
        {
            rfHungUp = TRUE;
        }
    }

    if (rpCmd->FRequireConnectRsp())
    {
        //  Do we want to set rfHungUp for all commands with NO CARRIER response
        if (RIL_RESULT_NOCARRIER == rpRsp->GetNotifyCode())
        {
            rfHungUp = TRUE;
        }
        else if (RIL_RESULT_ERROR == rpRsp->GetNotifyCode() &&
                 RIL_E_OPNOTALLOWED == rpRsp->GetError())
        {
            rfHungUp = TRUE;
        }
    }
    

    // If we're not supposed to ignore the response and the command succeeded,
    //    then we should parse the response data now (so that it can be used by the notification
    //    code below)
    if (!rpCmd->FIgnoreRsp())
    {
            (void)rpRsp->ParseOKOrErrorData(rpCmd, (RIL_RESULT_OK == rpRsp->GetNotifyCode()));
    }

    if (rpCmd->CmdOptIsSet(CMDOPT_SIMQUERY))
    {
        // We were just checking to see if the SIM is ready or not.  We only care about if the return
        // value is NOT READY or not in this case
        hrError = rpRsp->GetError();

        m_fSimReady = (hrError != RIL_E_NOTREADY);
        // If the SIM is not inserted, we can abort the polling for the SIM to be ready
        if (hrError == RIL_E_SIMNOTINSERTED)
        {
            m_fQuitReadyStateThread = TRUE;
            BroadcastRealBlobNotification(RIL_NOTIFY_SIMNOTACCESSIBLE, NULL, 0);
            UpdateSIMState(RIL_E_SIMNOTINSERTED);
        }

        if(RIL_RESULT_ERROR == rpRsp->GetNotifyCode())m_fSimReady = FALSE;
        if (m_fSimReady)
        {
            // Hey, as long as we got the PIN state, let's see if it's
            // locked or not.  Why not?
            DWORD dwReadyState = RIL_READYSTATE_SIM;
            void *pBlobSim = NULL;
            UINT cbBlobSim = 0;

            if (RIL_RESULT_OK == rpRsp->GetNotifyCode())
            {
                rpRsp->GetBlob(pBlobSim, cbBlobSim);
                TBD_ASSERT(cbBlobSim == sizeof(DWORD));

                if ((*(DWORD *) pBlobSim) == RIL_LOCKEDSTATE_READY)
                {
                    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("The phone was unlocked from boot"));
                    dwReadyState |= RIL_READYSTATE_UNLOCKED;
                    // Wake up the SIM unlocked thread
                    SetEvent(g_hSIMUnlockEvent);
                }
                else if((*(DWORD *) pBlobSim) != RIL_LOCKEDSTATE_UNKNOWN)SendReadyStateNotification(RIL_READYSTATE_SIM, TRUE);
            }

        }

        // Wake up the waiting thread
        SetEvent(m_hSimReadyEvent);
        fRet = TRUE;
        goto Error;
    }

    // If we got a CONNECT response, it must be a response to successful ATD or ATA -- change it to OK
    if (RIL_NOTIFY_CONNECT == rpRsp->GetNotifyCode())
    {
        rpRsp->ConnectToOK();

        // Some networks seem to require a brief delay after a data connection is made
        // before data can be sent via the connection. We pause here before sending the
        // connect response to the client. The delay is configured using a registry value.
        // If the value is not present, then there is no delay.
        DWORD dwTemp = 0;
        if (rpCmd->CmdOptIsSet(CMDOPT_DELAYCONNECTRSP) &&
            GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConnectResponseDelay"), &dwTemp))
        {
            Sleep(dwTemp);
        }
    }
    else
    {
        // For non-CONNECT responses, if we have a notification to send upon success, do it now
        pnd = rpCmd->GiveUpNotificationData();
        if (pnd)
        {
            if (RIL_RESULT_OK == rpRsp->GetNotifyCode() &&
                (!pnd->FDelayedInitFromRsp() || pnd->FinishInitFromRspBlob(*rpRsp)))
            {

                BroadcastNotification(pnd);
                TBD_ASSERT(NULL == pnd);
            }
            else
            {
                delete pnd;
                pnd = NULL;
            }
        }
    }

    // If we got an RIL_E_SIMNOTINSERTED or RIL_E_SIMFAILURE error, send out a "SIM isn't accessible" notification
    hrError = rpRsp->GetError();
    if (RIL_E_SIMNOTINSERTED == hrError || RIL_E_SIMFAILURE == hrError)
    {
        // Should never be used, since, this notification is also send above, when the PIN state check
        // returns SIM not inserted.
        (void)BroadcastRealBlobNotification(RIL_NOTIFY_SIMNOTACCESSIBLE, NULL, 0);
        UpdateSIMState(RIL_E_SIMNOTINSERTED);
    }
    if (RILERRORCLASS(hrError) == RIL_ERRORCLASS_SIM)
    {
        UpdateSIMState(hrError);
    }

    // Forward the response we got to the handle that sent the command
    rpRsp->GetBlob(pBlob, cbBlob);

    // If we just queried the equipment state, then make
    // sure the global on/off state is set appropriately.
    if (APIID_GETEQUIPMENTSTATE == rpCmd->GetAPIID() &&
        RIL_RESULT_OK == rpRsp->GetNotifyCode())
    {
        RILEQUIPMENTSTATE * pres;
        if (NULL != pBlob)
        {
            ASSERT(sizeof(RILEQUIPMENTSTATE) == cbBlob);

            // Take a peek at the response data
            pres = (RILEQUIPMENTSTATE *)pBlob;
            if (RIL_RADIOSUPPORT_OFF == pres->dwRadioSupport)
                rfRadioOff = TRUE;
            else if (RIL_RADIOSUPPORT_ON == pres->dwRadioSupport)
                rfRadioOff = FALSE;
        }
    }

    rpCmd->SendResponse(rpRsp->GetNotifyCode(), pBlob, cbBlob);
    rpRsp->DeleteBlob();
    pBlob = NULL;
    cbBlob = 0;
    fRet = TRUE;

    Error:
    delete pnd;
    delete rpCmd;
    delete rpRsp;
    rpCmd = NULL;
    rpRsp = NULL;
    return fRet;
}


//
//
//
BOOL CRilHandle::AppendReadBytes(COM_PORT_TYPE PortType, LPCSTR szBytes, UINT cbBytes)
{
    // TBD_FUNCTION(CRilHandle::AppendReadBytes);
    BOOL fRet = FALSE;

    if (!m_pReadBytes[PortType]) {
      m_pReadBytes[PortType] = new CBuffer;
    }

    if (m_pReadBytes[PortType])
    {
        fRet = m_pReadBytes[PortType]->Append(szBytes, cbBytes);
    }

    if (!fRet)
    {
        // Critically low on memory
        SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
    }
    return fRet;
}


//
//
//
BOOL CRilHandle::InheritReadBytes(COM_PORT_TYPE PortType, CResponse* const pRsp)
{
    // TBD_FUNCTION(CRilHandle::InheritReadBytes);
    BOOL fRet = FALSE;

    if (!m_pReadBytes[PortType]) {
      m_pReadBytes[PortType] = new CBuffer;
    }

    if (m_pReadBytes[PortType])
    {
        m_pReadBytes[PortType]->InheritData(pRsp);
        fRet = TRUE;
    }

    return fRet;
}


//
//
//
BOOL CRilHandle::GiveUpReadBytes(COM_PORT_TYPE PortType, LPSTR& rszBytes, UINT& rcbBytes)
{
    // TBD_FUNCTION(CRilHandle::GiveUpReadBytes);
    BOOL fRet = FALSE;

    if (!m_pReadBytes[PortType]) {
      m_pReadBytes[PortType] = new CBuffer;
    }

    if (m_pReadBytes[PortType])
    {
        rcbBytes = m_pReadBytes[PortType]->GetLength();
        rszBytes = m_pReadBytes[PortType]->GiveUpData();
        fRet = TRUE;
    }

    return fRet;
}


//
//
//
BOOL CRilHandle::SetEmergencyMode(const BOOL fMode)
{
    // TBD_FUNCTION(CRilHandle::SetEmergencyMode);
    SYNCBLOCK(m_csEmergency);

    DWORD dwNotificationCode;
    BOOL fRet = FALSE;

    if (m_fEmergencyMode && fMode)
    {
        // If we're trying to enter the emergency mode while already in emergency mode, fail
        goto Error;
    }
    TBD_ASSERT(FALSE == m_fEmergencyMode || FALSE == fMode);
    TBD_ASSERT(FALSE != m_fEmergencyMode || FALSE != fMode);

    // Set the new mode
    m_fEmergencyMode = fMode;

    if (m_fEmergencyMode)
    {
        // We just entered emergency mode
        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::SetEmergencyMode : Entering emergency mode"));

        // Clear the command queue, sending out "cancelled" responses to response callbacks
        g_pCmdQ[COMMAND_PORT]->Enum(GenerateCancelledRsp, (DWORD)this, TRUE);
        dwNotificationCode = RIL_NOTIFY_EMERGENCYMODEENTERED;
    }
    else
    {
        // We just exited emergency mode
        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::SetEmergencyMode : Exiting emergency mode"));
        dwNotificationCode = RIL_NOTIFY_EMERGENCYMODEEXITED;
    }

    // Send out an appropriate notification
    (void)BroadcastRealBlobNotification(dwNotificationCode, NULL, 0);
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CRilHandle::RegisterWithCPM()
{
    // TBD_FUNCTION(CRilHandle::RegisterWithCPM);
    BOOL fRet = FALSE;

    m_pMonitor = new CMonitor;
    if (!m_pMonitor || !m_pMonitor->Init(m_hCancelEvent))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete m_pMonitor;
        m_pMonitor = NULL;
    }
    return fRet;
}


//
//
//
void CRilHandle::GetAPIInfo(APIID apiid, APIINFO& rapii)
{
    // TBD_FUNCTION(CRilHandle::GetAPIInfo);
    TCHAR tszRegKey[MAX_PATH];
    char szID[40];
    APIINFO* pNewAPIInfo = NULL;
    DWORD dwDummy;

    // Intialize the output struct with defaults
    rapii.dwTimeout  = g_TimeoutAPIDefault;

    // Check index for cache array
    if ((apiid < 0) || (apiid >= APIID_TOTAL)) {
      goto Error;
    }

    // See if already loaded info for this API
    if (!m_rgpAPIInfo[apiid])
    {
        // Allocate new API info
        pNewAPIInfo = new APIINFO;
        if (!pNewAPIInfo)
        {
            goto Error;
        }

        // Compose the registry key
        (void)_itoa((int)apiid, szID, 10);
        (void)_tcsncpyz(tszRegKey, g_tszRegKeyAPIInfo, MAX_PATH);
        (void)_tcsncatz(tszRegKey, TString(szID), MAX_PATH);

        // Exception for the default timeouts: the dial commands should be aborted only by the user or the
        // network, not by the RIL. For this reason the registry Override=dword:1 has to be set, when the
        // RIL should also have an timeout value.
        if (apiid == APIID_DIAL && !GetRegistryDWORD(HKEY_LOCAL_MACHINE, tszRegKey, TEXT("Override"), &dwDummy))
        {
            pNewAPIInfo->dwTimeout  = INFINITE;
        }
        else if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, tszRegKey, TEXT("Timeout"), &pNewAPIInfo->dwTimeout))
        {
            // Just use the default
            pNewAPIInfo->dwTimeout  = g_TimeoutAPIDefault;
        }

        // If the timeout is specified as 0 milliseconds, assume INFINITE timeout
        if (!pNewAPIInfo->dwTimeout)
        {
            pNewAPIInfo->dwTimeout = INFINITE;
        }

        // Cache the data we just read
        m_rgpAPIInfo[apiid] = pNewAPIInfo;
        pNewAPIInfo = NULL;
    }

    TBD_ASSERT(NULL != m_rgpAPIInfo[apiid]);
    rapii = *m_rgpAPIInfo[apiid];

    Error:
    delete pNewAPIInfo;

    //TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("CRilHandle::GetAPIInfo : Info for API %d: timeout: %d msec"), apiid, rapii.dwTimeout);
}


//
// Used for cleaning up notification items
// when the notification queue is destroyed.
//
void NotificationItemDtor(void *pItem)
{
    LocalFree(pItem);
}

//
// Instance handle ctor
//
CRilInstanceHandle::CRilInstanceHandle()
: CListElem(),
m_pDevice(NULL),
m_pCmdList(NULL),
m_hNotifyCancelEvent(NULL),
m_notificationQ(NotificationItemDtor),
m_fInited(FALSE),
m_fPreferred(FALSE)
{
    //TBD_FUNCTION(CRilInstanceHandle::CRilInstanceHandle);
}


//
// Instance handle dtor
//
CRilInstanceHandle::~CRilInstanceHandle()
{
    //TBD_FUNCTION(CRilInstanceHandle::~CRilInstanceHandle);
    if (m_fPreferred)
    {
        // Switch RIL out of emergency mode
        (void)m_pDevice->SetEmergencyMode(FALSE);

        // This handle is not preferred anymore
        m_fPreferred = FALSE;
    }

    CancelNotifications();

    delete m_pCmdList;
    m_pCmdList = NULL;

    if (m_hNotifyCancelEvent)
    {
        (void)CloseHandle(m_hNotifyCancelEvent);
        m_hNotifyCancelEvent = NULL;
    }
    m_fInited = FALSE;
}


//
// Initalization
//
BOOL CRilInstanceHandle::Init(CRilHandle* const pDevice)
{
    //TBD_FUNCTION(CRilInstanceHandle::Init);
    BOOL fRet = FALSE;

    if (pDevice->FInEmergencyMode())
    {
        // We can't create any additional RIL instances in emergency mode
        TBD_ASSERT(FALSE == m_fPreferred);
        goto Error;
    }

    // Create command list
    m_pCmdList = new CDblList<CCommand>;
    if (!m_pCmdList)
    {
        goto Error;
    }

    m_pDevice = pDevice;
    m_fInited = TRUE;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
void CRilInstanceHandle::Notify(const DWORD dwCode, const HRESULT hrCmdID, const void* const pBlob,
                                const UINT cbBlob)
{
    //TBD_FUNCTION(CRilInstanceHandle::Notify);
    TBD_ASSERT(FALSE != m_fInited);

    RILDRVNOTIFICATION* prdn = NULL;
    UINT cbTotal;

    // Alocate a notification data structure
    // NOTE: LocalAlloc() is used to prevent allocated memory from being tracked
    //       (since it may be freed by a thread coming from RIL proxy)
    cbTotal = sizeof(RILDRVNOTIFICATION) + cbBlob;
    prdn = (RILDRVNOTIFICATION*)LocalAlloc(0, cbTotal);
    if (!prdn)
    {
        goto Error;
    }

    // Fill in the structure fields
    prdn->cbSize = cbTotal;
    prdn->cbSizeNeeded = cbTotal;
    prdn->dwCode = dwCode;
    prdn->hrCmdID = hrCmdID;
    if (cbBlob)
    {
        memcpy(prdn->pbData, pBlob, cbBlob);
    }

    // Try to queue the structure into the Notification Queue
    // (NOTE: this will fail if the queue is already full -- this guarantees that this RIL thread can't
    //        be blocked by a hanging RIL client)
    if (!m_notificationQ.Put(prdn))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilInstanceHandle::Notify : Dropped RIL notification - out of memory? (0x%x)"), this);
        goto Error;
    }

#ifdef TBD_DEBUG
    if (m_notificationQ.GetSize() > 10)
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilInstanceHandle::Notify : Notify queue for handle 0x%x has size %d"), this, m_notificationQ.GetSize());
    }
#endif

    // The queue took ownership of the notification data
    //prdn = NULL;
    return;

    Error:
    // NOTE: Since we allocated this structure using LocalAlloc(), we need to use LocalFree() to delete it
    //       (or else the memory tracker will assert)
    LocalFree(prdn);
}


//
// Function passed to CQueue::ConditionalGet() below
//
BOOL FEnoughStorage(void* pItem, DWORD dwData)
{
    //TBD_FUNCTION(FEnoughStorage);
    TBD_ASSERT(NULL != pItem);

    RILDRVNOTIFICATION* prdnNextNotification = (RILDRVNOTIFICATION*)pItem;
    RILDRVNOTIFICATION* prdnSuppliedStruct   = (RILDRVNOTIFICATION*)dwData;

    prdnSuppliedStruct->cbSizeNeeded = prdnNextNotification->cbSizeNeeded;
    return (prdnSuppliedStruct->cbSize >= prdnSuppliedStruct->cbSizeNeeded);
}


//
//
//
BOOL CRilInstanceHandle::GetNextNotification(RILDRVNOTIFICATION& rrdnSuppliedStruct)
{
    //TBD_FUNCTION(CRilInstanceHandle::GetNextNotification);
    TBD_ASSERT(FALSE != m_fInited);

    RILDRVNOTIFICATION* prdnNextNotification = NULL;
    UINT cbBlob;
    HRESULT hr;
    BOOL fRet = FALSE;

    // Initialize cbSizeNeeded field
    rrdnSuppliedStruct.cbSizeNeeded = rrdnSuppliedStruct.cbSize;

    // Get the next notification from the queue, if the provided size is sufficient
    hr = m_notificationQ.ConditionalGet(FEnoughStorage, (DWORD)&rrdnSuppliedStruct, &prdnNextNotification);
    if (E_FAIL == hr)
    {
        // The supplied size wasn't enough - return TRUE
        fRet = TRUE;
        goto Error;
    }
    else if (S_FALSE == hr)
    {
        goto Error;
    }

    // We shouldn't have to worry about any error result other than E_FAIL.
    TBD_ASSERT(SUCCEEDED(hr));

    // The supplied size is enough, so we retrieved the next notification
    TBD_ASSERT(NULL != prdnNextNotification);
    TBD_ASSERT(prdnNextNotification->cbSize == prdnNextNotification->cbSizeNeeded);

    rrdnSuppliedStruct.dwCode = prdnNextNotification->dwCode;
    rrdnSuppliedStruct.hrCmdID = prdnNextNotification->hrCmdID;
    cbBlob = prdnNextNotification->cbSize - sizeof(RILDRVNOTIFICATION);
    if (cbBlob)
    {
        memcpy(rrdnSuppliedStruct.pbData, prdnNextNotification->pbData, cbBlob);
    }
    fRet = TRUE;

    Error:
    (void)LocalFree(prdnNextNotification);
    return fRet;
}


//
//
//
BOOL CRilInstanceHandle::MakePreferred()
{
    //TBD_FUNCTION(CRilInstanceHandle::MakePreferred);
    TBD_ASSERT(NULL != m_pDevice);

    BOOL fRet = FALSE;

    // Switch RIL into emergency mode
    if (!m_pDevice->SetEmergencyMode(TRUE))
    {
        goto Error;
    }

    // Mark this handle as preferred
    m_fPreferred = TRUE;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CRilInstanceHandle::CreateNotificationQueue(LPCTSTR tszCancelEventName,
                                                 PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut)
{
    //TBD_FUNCTION(CRilInstanceHandle::CreateNotificationQueue);
    TBD_ASSERT(FALSE != m_fInited);

    BOOL fRet = FALSE;
    DWORD dwOut = 0;

    if (!pBufOut || !dwLenOut || !pdwActualOut)
    {
        goto Error;
    }

    // Initialize Notification Q
    if (!m_notificationQ.Init(5))
    {
        goto Error;
    }

    // Create the cancel event
    m_hNotifyCancelEvent = CreateEvent(NULL, TRUE, FALSE, tszCancelEventName);
    if (!m_hNotifyCancelEvent)
    {
        goto Error;
    }
    TBD_ASSERT(ERROR_ALREADY_EXISTS == GetLastError());

    dwOut = _tcslen(m_notificationQ.GetItemsEventName()) * sizeof(TCHAR) + sizeof(TCHAR);
    if (dwOut > dwLenOut)
    {
        goto Error;
    }

    _tcscpy((TCHAR*)pBufOut, m_notificationQ.GetItemsEventName());
    *pdwActualOut = dwOut;


    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
DWORD CRilHandle::AckSMSThread()
{
    DWORD dwEvent;

    while (1)
    {
        dwEvent = WaitForSingleObject(g_hAckSMSEvent, INFINITE);
        // See if the thread needs to terminate
        if (FCancelSet())
        {
            break;
        }
        if(dwEvent == WAIT_OBJECT_0)
        {
            g_dwLastSMSCmd = GetTickCount();

            QueueInternalCmd(URC_PORT, this, "AT+CNMA=0\r", CMDOPT_NONE|CMDOPT_SMSACK, APIID_SENDMSGACKNOWLEDGEMENT, 0, 0, 0);
        }
    }

    return 0;
}

//
//
//
DWORD WINAPI AckSMSThreadProc(LPVOID lpParameter)
{
    //TBD_FUNCTION(AckSMSThreadProc);
    TBD_ASSERT(lpParameter != NULL);
    CRilHandle* pRilDevice = (CRilHandle*)lpParameter;

    if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, (TEXT("AckSMSThreadProc : Unable to raise priority of read thread!!")));
        return 0;
    }

    TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("AckSMSThreadProc : Entering, pRilDevice=0x%x"),pRilDevice);
    const DWORD dwReturn = pRilDevice->AckSMSThread();
    TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("AckSMSThreadProc : THREAD IS EXITING, dwReturn=0x%x"),dwReturn);
    return dwReturn;
}

//
//
//
HRESULT ParseGetNumPBEntries(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetNumPBEntries);
    HRESULT hr = S_OK;
    UINT nValue;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CPBR: <range>,n,n<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CPBR: (", szRsp)  ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (!MatchStringBeginning(szRsp, "-", szRsp)) {
        g_iNumPBEntries = nValue;
    }
    else
    {
        if (!ParseUInt(szRsp, TRUE, g_iNumPBEntries, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    if (!MatchStringBeginning(szRsp, "),", szRsp)        ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)           ||
        !MatchStringBeginning(szRsp, ",", szRsp)         ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)           ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if(g_iNumPBEntries > 8)g_iNumPBEntries = 8;

Error:
#ifdef TBD_DEBUG
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Number of own number PB entries = %d"), g_iNumPBEntries);
#endif
    return hr;
}

//
//
//
HRESULT ParseGetOwnPBEntries(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetOwnPBEntries);
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    char szAddress[MAXLENGTH_ADDRESS];
    WCHAR wszText[MAXLENGTH_PHONEBOOKTEXT];
    RILPHONEBOOKENTRY* rgrbpe = NULL;
    HRESULT hr = S_OK;
    UINT i;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<command>"
    while (MatchStringBeginning(szRsp, "+CPBR: ", szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrbpe, sizeof(RILPHONEBOOKENTRY), nUsed, &nAllocated, MISC_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrbpe[nUsed], 0x00, sizeof(RILPHONEBOOKENTRY));
        rgrbpe[nUsed].cbSize = sizeof(RILPHONEBOOKENTRY);

        // Parse "<index>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        rgrbpe[nUsed].dwIndex = nValue;
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_INDEX;

        // Parse ",<number>,<type>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)     ||
            !MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) ||
            !StringToRILAddress(szAddress, (BYTE)nValue, rgrbpe[nUsed].raAddress)) {
            goto Continue;
        }
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_ADDRESS;

        // Parse ",<text>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseQuotedEncodedString(ENCODING_UCS2, szRsp, wszText, wszText + MAXLENGTH_PHONEBOOKTEXT)) {
            goto Continue;
        }
        (void)wcsncpyz(rgrbpe[nUsed].wszText, wszText, MAXLENGTH_PHONEBOOKTEXT);
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_TEXT;

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    // Copy the PB entries into the subscriber structure
    for(i=0;i<nUsed;i++)
    {
        memset((void*)&g_rsi[i], 0x00, sizeof(RILSUBSCRIBERINFO));
        g_rsi[i].cbSize = sizeof(RILSUBSCRIBERINFO);
        if(rgrbpe[i].dwParams&RIL_PARAM_PBE_ADDRESS)
        {
            memcpy((void*)&g_rsi[i].raAddress, (void*)&rgrbpe[i].raAddress, sizeof(RILADDRESS));
            g_rsi[i].dwParams |= RIL_PARAM_SI_ADDRESS;
#ifdef TBD_DEBUG
            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Subscriber address = %s"), &g_rsi[i].raAddress.wszAddress);
#endif
        }
        if(rgrbpe[i].dwParams&RIL_PARAM_PBE_TEXT)
        {
            wcscpy(g_rsi[i].wszDescription, rgrbpe[i].wszText);
            g_rsi[i].dwParams |= RIL_PARAM_SI_DESCRIPTION;
#ifdef TBD_DEBUG
            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Subscriber text = %s"), &g_rsi[i].wszDescription);
#endif
        }
    }
    // Force the first number to be the voice number
    g_rsi[0].dwService = RIL_SERVICE_VOICE;
    g_rsi[0].dwAddressId = 1;
    g_rsi[0].dwParams |= (RIL_PARAM_SI_SERVICE|RIL_PARAM_SI_ADDRESSID);

    g_rsiused = nUsed;
#ifdef TBD_DEBUG
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Number of subscriber entries = %d"), nUsed);
#endif

Error:
    delete[] (BYTE*)rgrbpe;

    return hr;
}

//
//
//
BOOL CRilHandle::FetchOwnNumber()
{
    BOOL bSuccess;
    char szCmd[MAX_ATCMD_LEN];

    // Clear any previous entries
    g_iNumPBEntries = 0;
    g_rsiused = 0;
    memset((void*)&g_rsi[0], 0x00, sizeof(RILSUBSCRIBERINFO));
    bSuccess = FALSE;

    if (!QueueInternalCmdBlocking(COMMAND_PORT, this, "AT+CPBS?\r", CMDOPT_NONE, APIID_GETPHONEBOOKOPTIONS, 0, 16, 2000, ParseGetPhonebookOptions))
        goto Error;

    if(g_dwModemType == MODEMTYPE_SIEMENS_MC45 || g_dwModemType == MODEMTYPE_SIEMENS_MC46)
    {
        // The MC4x modems don't support the AT+CNUM commands. As a workaround we preread the
        // own numbers now.

        // Select the own number phonebook
        if (!QueueInternalCmd(COMMAND_PORT, this, "AT+CPBS=\"ON\"\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
            goto Error;

        // Check if there are any entries
        if (!QueueInternalCmdBlocking(COMMAND_PORT, this, "AT+CPBR=?\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 16, 2000, ParseGetNumPBEntries))
            goto Error;

        // Read out the numbers
        if(g_iNumPBEntries==0)(void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBR=1,1\r");
        else (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBR=1,%d\r", g_iNumPBEntries);

        if (!QueueInternalCmd(COMMAND_PORT, this, szCmd, CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0, ParseGetOwnPBEntries))
            goto Error;

        bSuccess = TRUE;
    }
    else
    {
        // We got the AT+CNUM command, so we do not need to pre-read the own numbers.
        // What we do is to try AT+CPBR=? until the SIM busy error disapears. This
        // way we now, that the SIM is completely ready now.

        // Select the SIM phonebook (there would no use to access the ME memory, since
        // this is always ready).
        if (!QueueInternalCmd(COMMAND_PORT, this, "AT+CPBS=\"SM\"\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 0, 0))
            goto Error;

        // Check if there are any entries
        if (!QueueInternalCmdBlocking(COMMAND_PORT, this, "AT+CPBR=?\r", CMDOPT_NONE, APIID_UNLOCKPHONE, 0, 16, 2000, ParseGetNumPBEntries))
            goto Error;

        bSuccess = TRUE;
    }

Error:
    if (!bSuccess) {
        RILRetailTrace((TEXT("RilDrv: Failed to read own number.\r\n")));
    }

    if (g_dwCurrentPhoneBook != RIL_PBLOC_UNKNOWN && g_dwCurrentPhoneBook <= NUM_PBLOCS) {
        _snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBS=\"%s\"\r", g_rgszPBLocations[g_dwCurrentPhoneBook]);
        QueueInternalCmd(COMMAND_PORT, this, szCmd, CMDOPT_NONE, APIID_SETPHONEBOOKOPTIONS, 0, 16, 2000);
    }
    return bSuccess;
}

void CRilHandle::EnterUSSDAnswerMode(COM_PORT_TYPE PortType)
{
    RILRetailTrace((TEXT("RilDrv: USSD Answer Mode entered.\r\n")));
    m_fUSSDAnswerMode[PortType] = TRUE;
}

void CRilHandle::LeaveUSSDAnswerMode(COM_PORT_TYPE PortType, BYTE* pData, DWORD dwSize)
{
    const BYTE bCtrlZ = 26;
    DWORD dwDummy;

    if (m_fUSSDAnswerMode[PortType])
    {
        m_pComDevice[PortType]->VirtWrite(pData, dwSize, dwDummy);
        RILRetailTrace((TEXT("RilDrv: USSD Answer Mode: sending %a.\r\n"), pData)); // pData is a zero-terminated string.
        m_pComDevice[PortType]->VirtWrite(&bCtrlZ, 1, dwDummy);
        RILRetailTrace((TEXT("RilDrv: USSD Answer Mode: sending Ctrl-Z.\r\n")));
    }
    RILRetailTrace((TEXT("RilDrv: USSD Answer Mode left.\r\n")));
    m_fUSSDAnswerMode[PortType] = FALSE;
}

void CRilHandle::CancelUSSDAnswerMode(COM_PORT_TYPE PortType)
{
    const BYTE bESC = 27;
    DWORD dwDummy;

    if (m_fUSSDAnswerMode[PortType]) {
        m_pComDevice[PortType]->VirtWrite(&bESC, 1, dwDummy);
        RILRetailTrace((TEXT("RilDrv: USSD Answer Mode: sending ESC.\r\n")));
    }
    m_fUSSDAnswerMode[PortType] = FALSE;
    RILRetailTrace((TEXT("RilDrv: USSD Answer Mode canceled.\r\n")));
    Sleep(250);
}

INT CRilHandle::GetPortInUSSDAnswerMode()
{
    INT iPort;

    for (iPort = 0; iPort < NUMBER_OF_PORTS; iPort++) {
        if (m_fUSSDAnswerMode[iPort])
            break;
    }

    return iPort;
}

COM_PORT_TYPE CRilHandle::GetFreeAlternativeCmdPort(BOOL bAllowAuxDataPort)
{
    COM_PORT_TYPE iPort = DATA_PORT;

    if (GetComDevice(DATA_PORT)->FDataMode() || GetDataAllowed()) {
        iPort = COMMAND_PORT;
    } else if (bAllowAuxDataPort && (INVALID_PORT != g_ComManager.GetAuxDataPort())) {
        iPort = g_ComManager.GetAuxDataPort();
    }
    return iPort;
}

#ifdef WANT_RILDRV_OUTPUT_TIMESTAMP
void NKDbgWithTimeStamp(LPWSTR lpszFmt, ...)
{
    va_list ap;
    WCHAR wszBuffer[1024];
    WCHAR *pWC;
    WCHAR   UniBuffer2[1024];
    
    wcscpy(UniBuffer2, lpszFmt);
    pWC = wcsstr(UniBuffer2, L"%a");
    if (pWC != NULL)
        wcsncpy(pWC, L"%S", 2);
    va_start(ap, lpszFmt);
    wvsprintf(wszBuffer, UniBuffer2, ap);
    va_end(ap);

    NKDbgPrintfW(L"%06d %s", GetTickCount()%1000000,wszBuffer);
}
#endif

#ifdef WANT_FILE_RILDRV_OUTPUT
int DbgWrite(LPWSTR lpszFmt, ...)
{
    int count, i;
    va_list ap;
    WCHAR *pWC;
    WCHAR   UniBuffer[1024];
    WCHAR   UniBuffer2[1024];
    char    AnsiBuffer[1024];
    char    time[32];
    DWORD   dwBytes;
    HANDLE  hDebugFile;

    if(g_dwDisableDebugFile == 1)return 0;

    wcscpy(UniBuffer2, lpszFmt);
    pWC = wcsstr(UniBuffer2, L"%a");
    if (pWC != NULL)
        wcsncpy(pWC, L"%S", 2);
    va_start(ap, lpszFmt);
    count = wvsprintf(UniBuffer, UniBuffer2, ap);
    va_end(ap);

    if (count > 0 && count < 1024)
    {
        EnterCriticalSection(&csDbgCritSect);
        hDebugFile = CreateFile((TEXT("\\Storage Card\\RilDbg.log")), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
        if(hDebugFile == (HANDLE)-1)
        {
            LeaveCriticalSection(&csDbgCritSect);
            return 0;
        }

        SetFilePointer(hDebugFile, 0, 0, FILE_END );
        sprintf(time, "% 8dms : ", GetTickCount());
        WriteFile(hDebugFile, time, strlen(time), &dwBytes, NULL);
        for(i=0; i<count; i++)AnsiBuffer[i] = (char)UniBuffer[i];

        WriteFile(hDebugFile, AnsiBuffer, count, &dwBytes, NULL);

        CloseHandle(hDebugFile);
        LeaveCriticalSection(&csDbgCritSect);
    }
    return count;
}
#endif // WANT_FILE_RILDRV_OUTPUT

